首页 > 编程知识 正文

同步阻塞IO 和 IO多路复用,非阻塞io多路复用

时间:2023-05-03 22:40:32 阅读:231443 作者:393

        RPC 是解决 跨计算机的 进程间通信 的一种方式。一次 RPC 调用,本质就是  服务 消费者 服务 提供者间的一次网络信息交换的过程。说到网络通信,就要提到网络 IO 模型。为什么要讲网络 IO 模型呢?因为 所谓的两台 PC 机之间的网络通信,实际上就是两台 PC 机对网络 IO 的操作

        最常用的两种网络IO模型是 同步阻塞IOIO多路复用。 分别用两个比喻来说明这两种IO模型的区别:

        同步阻塞IO: 我们去餐厅吃饭,我们到达餐厅,向服务员点餐,之后要一直在餐厅等待后厨将菜做好,然后服务员会将菜端给我们,我们才能享用。

        IO多路复用:我们去餐厅吃饭,这次我们是几个人一起去的,我们专门留了一个人在餐厅排号等位,其他人就去逛街了,等排号的朋友通知我们可以吃饭了,我们就直接去享用了。

        同步阻塞IO 和 IO多路复用 ,都是同步的 ,也就是 都需要等待 到数据到来,才能做下一步的处理。 

一、常见的网络IO模型

        常见的网络 IO 模型分为四种:同步阻塞 IO(BIO)、同步非阻塞 IO(NIO)、IO 多路复用异步非阻塞 IO(AIO)。

        在这四种 IO 模型中,只有 异步非阻塞IO(AIO) 为异步 IO,其他都是同步 IO。

二、同步阻塞IO

        同步阻塞 IO 是 最简单、最常见的 IO 模型,在 Linux 中,默认情况下所有的 socket 都是 blocking 的,先看下操作流程。

        首先,应用进程发起 IO 系统调用后,应用进程被阻塞,转到内核空间处理。 之后,内核开始等待数据,等待到数据之后,再将内核中的数据拷贝到用户内存中,整个 IO 处理完毕后返回进程。最后应用的进程解除阻塞状态,运行业务逻辑。

        系统内核处理 IO 操作 分为两个阶段—— 等待数据拷贝数据。而在这两个阶段中,应用进程中 IO 操作的线程会一直都处于阻塞状态,如果是基于多线程开发,那么每一个 IO 操作都要占用线程,直至 IO 操作结束。

三、IO多路复用

        多路复用 IO 是 在高并发场景中使用最为广泛的一种 IO 模型,如 Java 的 NIO、Redis、Nginx 的底层实现就是此类 IO 模型的应用,经典的 Reactor 模式也是基于此类 IO 模型。

        IO多路复用 字面上的理解,多路 就是 指多个通道,也就是多个网络连接的 IO,而复用就是指多个通道复用在一个复用器上。   多个网络连接的 IO 可以注册到一个复用器(select)上,当用户进程调用了 select,那么整个进程会被阻塞。同时,内核会“监视”所有 select 负责的 socket,当任何一个 socket 中的数据准备好了,select 就会返回。这个时候用户进程再调用 read 操作,将数据从内核中拷贝到用户进程。

        当用户进程发起了 select 调用,进程会被阻塞,当发现该 select 负责的 socket 有准备好的数据时才返回,之后才发起一次 read,整个流程要比阻塞 IO 要复杂,似乎也更浪费性能。但它最大的优势在于,用户可以在一个线程内同时处理多个 socket 的 IO 请求。             用户可以注册多个socket,然后不断地调用 select 读取被激活的 socket,即可达到在同一个线程内同时处理多个 IO 请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

        RPC 调用在大多数的情况下,是一个高并发调用的场景,考虑到系统内核的支持、编程语言的支持以及 IO 模型本身的特点,在 RPC 框架的实现中,在网络通信的处理上,我们会选择 IO 多路复用的方式。开发语言的网络通信框架的选型上,我们最优的选择是基于 Reactor 模式实现的框架,如 Java 语言,首选的框架便是 Netty 框架(Java 还有很多其他 NIO 框架,但目前 Netty 应用得最为广泛),并且在 Linux 环境下,也要开启 epoll 来提升系统性能(Windows 环境下是无法开启 epoll 的,因为系统内核不支持)。

       

四、网络IO的读写流程 和 零拷贝

        系统内核处理 IO 操作分为两个阶段——等待数据拷贝数据。等待数据,就是系统内核在等待网卡接收到数据后,把数据写到内核中;而拷贝数据,就是系统内核在获取到数据后,将数据拷贝到用户进程的空间中。

        应用进程的每一次写操作,都会把数据写到用户空间的缓冲区中,再由 CPU 将数据拷贝到系统内核的缓冲区中,之后再由 DMA 将这份数据拷贝到网卡中,最后由网卡发送出去。应用进程的一次完整的读写操作,都需要在用户空间与内核空间中来回拷贝,并且每一次拷贝,都需要 CPU 进行一次上下文切换。 这样很浪费 CPU 和性能, 那有没有什么方式,可以减少进程间的数据拷贝,提高数据传输的效率呢?

        这时就需要 零拷贝(Zero-copy)技术 。 所谓的 零拷贝,就是 取消用户空间与内核空间之间的数据拷贝操作,应用进程每一次的读写操作,都可以通过一种方式,让应用进程向用户空间写入或者读取数据,就如同直接向内核空间写入或者读取数据一样,再通过 DMA 将内核中的数据拷贝到网卡,或将网卡中的数据 copy 到内核。

零拷贝 有两种解决方式,分别是 mmap+write 方式和 sendfile 方式,mmap+write 方式的核心原理就是通过虚拟内存来解决的。

零拷贝 的好处 就是 避免没必要的 CPU 拷贝,让 CPU 解脱出来去做其他的事,同时也减少了 CPU 在用户空间与内核空间之间的上下文切换,从而提升了网络通信效率与应用程序的整体性能。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。