操作系统下常见的四种网络I/O模型详解

操作系统中,常见的I/O模型包括以下几种:

  • 阻塞I/O
  • 非阻塞I/O
  • I/O多路复用
  • 异步I/O
    对于网络I/O来说,会涉及到两个系统级别的对象即就是:调用当前I/O的进程以及系统内核。当一个读操作发生的时候,会经历两个阶段:1. 等待数据的准备 2. 将数据从内核拷贝到进程中。

阻塞式I/O

一般情况下,操作系统中所有的socket默认的是阻塞的,当用户调用了系统套接字recvfrom的时候,内核会就会进入I/O的第一个阶段,就是准备数据,对于网络I/O来说,这时候还有许多数据并未到达,那么内核就需要等待所有的数据到来,这时候用户进程将会被阻塞住,直到数据全部到来,内核将数据从缓冲区拷贝到用户内存,然后内核返回数据。阻塞式I/O的特点是I/O执行的两个阶段(数据准备阶段以及内核将数据复制的阶段)用户进程都被阻塞住。
操作系统下常见的四种网络I/O模型详解

非阻塞式I/O

相比于阻塞式I/O,非阻塞式I/O做了一些改进,调用套接字recvform的时候在内核准备数据的阶段,如果数据没有准备好,用户进程不会立刻被阻塞而是,会立即反馈给用户一个error信息,那么用户就可以轮询的调用recvform监听内核,如果用户的数据都准备好了,且这时候用户刚好调用了recvform,那么会立即将数据从内核缓冲区拷贝到用户进程中。在不断轮询的过程中,会耗费大量的CPU资源。
操作系统下常见的四种网络I/O模型详解

I/O多路复用

I/O多路复用时使用最为广泛的一种技术,可以通过单个进程监听多个I/O事件。通过select / poll / epoll 这些函数轮询的访问I/O事件,如果某个I/O事件到达了,那么就会通知用户进程。
操作系统下常见的四种网络I/O模型详解

select / poll / epoll的区别

select的方式

首先需要明确的一点就是,调用select的时候,整个用户的进程也会被阻塞。select是通过轮询的方式遍历存放I/O事件描述符的数据结构完成对每个I/O事件的监听;且单个进程监听的I/O事件的数量也有很大的限制,32bit机器默认的是2014,而64位机器则默认的是2048,同时,通过轮询的访问存放I/O事件描述符的数据结构的方法是非常耗费CPU资源的一种方式,用户空间于内核空间之间复制的成本是非常高的。

poll的方式

poll的本质与select是没有多大差距的,它将用户传入的数组拷贝到内核空间,然后查询每个I/O事件描述符对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有I/O事件描述符后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历。

epoll的方式

epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些I/O事件描述符刚刚变为就需态,并且只会通知一次。没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口,效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;
即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。

异步I/O

用户进程发起读操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何阻塞。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
操作系统下常见的四种网络I/O模型详解