天天看点

你应该知道的五种IO模型 IO模型Select&Poll&Epoll

点击上方蓝色字体,选择“设为星标”

优质文章,及时送达

你应该知道的五种IO模型 IO模型Select&Poll&Epoll

写在前面

linux操作系统包含了五种IO模型,各种上层编程语言或者网络编程框架的上层实现都是基于操作系统的这些IO实现来实现的。

五种IO模型主要围绕:阻塞IO,非阻塞IO,异步IO来展开。

IO的处理流程

对于一次IO操作,数据先拷贝到内核空间,之后从内核空间拷贝到用户空间,所以一次数据读取操作经过两个阶段:

1. 等待数据就绪

2. 数据从内核空间拷贝到用户空间

IO模型

阻塞IO

进程发起IO调用,一直等待数据就绪和数据拷贝阶段完成,处于阻塞等待挂起中。

你应该知道的五种IO模型 IO模型Select&Poll&Epoll

阻塞IO

非阻塞IO

进程进行read操作时,如果数据没有准备好就返回,通过轮训方式反复调用内核问“有数据了没?”,如果数据准备好了则将数据从内核空间拷贝到用户空间,调用过程第一阶段非阻塞。

你应该知道的五种IO模型 IO模型Select&Poll&Epoll

非阻塞IO

多路复用IO

多个连接(fd)sockedFd或者fd就属于一路,多路就是多个fd连接,通过一个select轮训多个IO判断是否有就绪事件,如果读事件准备好就进行数据读取,将数据从内核空间拷贝到用户空间,数据拷贝阶段仍然阻塞。

你应该知道的五种IO模型 IO模型Select&Poll&Epoll

多路复用

信号驱动IO

进行发起读取事件调用后立即返回,当数据就绪之后会以信号的方式通知进程,之后进程再次发起读取操作,将数据从内核空间拷贝到用户空间,数据拷贝阶段仍然阻塞。

你应该知道的五种IO模型 IO模型Select&Poll&Epoll

信号驱动IO

异步IO

以上几种IO在数据复制阶段仍然是同步的,所以都属于某种程度的同步IO不是真正的异步IO。异步IO是进程发起读取操作后立即返回,等数据就绪之后,内核会将数据直接从内核空间拷贝到用户空间,并通知进程数据已经OK。这样在数据拷贝阶段就不是同步的了,实现了真正的异步。

你应该知道的五种IO模型 IO模型Select&Poll&Epoll

异步IO

IO模型对比

同步和异步IO的区别在于系统调用的recvfrom()方法是否阻塞。

Select&Poll&Epoll

由于异步IO在linux操作系统层面支持还有限,所以多路复用IO是大部分网络框架采用比较多的IO模型,其底层支持主要围绕select&poll&epoll实现。

epoll是linux2.6引入的新型一部IO多路复用技术,他的前辈是poll,所以我们现聊下poll。

在异步IO出现之前,所有IO都是同步的。由于IO操作较慢,所以会造成阻塞,浪费大量CPU时间,所以就非常低效,于是就出现了poll模型。

select

poll模型有两个函数:select和poll。

select的缺点在于受限于文件描述符的大小,一个进程默认控制在1024文件描述符内,在判断就绪文件描述符时,需要遍历所有文件描述符以找到就绪描述符,成本巨大。

poll

poll没有最大文件描述符大小限制,但是和select一样,只能监听到几个文件描述符就绪了,得遍历所有文件描述符获取就绪的IO。

1. 每次循环select都需要对fd指向的设备进行poll操作,如果fd非常多时,比如超多网络IO,就会显得非常低效,所以poll会限制每个select注册的最大文件数。

2. 在进行消息通知时,select采用的是内存拷贝的方式,也会影响IO效率。

poll函数需要设备驱动程序的支持,用户进程使用select对每个fd(文件描述符)对应的设备使用poll函数,查询每个fd是否有读写事件需要处理,如果有就会返回需要处理的事件的个数,如果没有就将select进程放到等待队列进行休眠等待直到超时或是被唤醒。这个唤醒是设备驱动实现的,每次有数据到达时,驱动程序就去check是否有进程在读写等待队列里,有就将其唤醒。

epoll

在poll模型中,select只能返回就绪的fd数量,用户进程还需要去对所有fd进行遍历才能处理事件。为了解决这些问题,就引入了epoll。

epoll没有最大描述符大小限制,通过回调机制,一旦某个文件描述符就绪了,就迅速激活这个文件描述符,当进程下一次调用epoll_wait()时候便得到了通知。

epoll对poll的数据结构进行了增强,允许用户传入自定义的数据结构,这样可以快速定位事件位置了。所以在大量空闲连接的时候,epoll效率就要高很多。

poll和epoll都采用轮训方式实现异步IO,并未实现基于中断达到真正的异步,所以这种异步只是Non-Blocking IO,并不是真正的异步IO。

春哥叨叨实验室