I/O模型
Unix提供了5种不同的I/O模型:
- 阻塞I/O
- 非阻塞I/O
- I/O复用
- 信号驱动I/O
- 异步I/O
一个输入操作通常包括两个阶段:
- 等待数据准备好
- 从内核向进程复制数据
(1)阻塞I/O(blocking I/O)
应用进程调用recvfrom,直到内核将数据准备好并复制数据到应用程序缓冲区或者发生错误才返回。最常见的错误就是系统调用被信号中断。进程从调用recvfrom开始到它返回的整段时间内都是被阻塞的。recvfrom成功返回后,应用进程开始处理数据。
(2)非阻塞I/O(nonblocking I/O)
进程把一个套接字设置成非阻塞是在通知内核:当请求的I/O操作非得让进程睡眠才能完成时,不要让进程睡眠,而是返回一个错误。
前三次调用recvfrom没有数据返回,内核立即返回一个EWOULDBLOCK错误。第四次调用recvfrom时,内核将数据准备好并复制数据到应用程序缓冲区,recvfrom成功返回,应用进程接着处理数据。
当一个应用程序对一个非阻塞描述符循环调用recvfrom,我们称之为轮询(polling)。应用进程持续轮询内核,以查看某个操作是否就绪,这么做往往耗费大量CPU时间。
(3)IO复用(I/O multiplexing)
调用select或poll,阻塞在这两个系统调用中的某一个之上,而不是阻塞在真正的I/O系统调用上。
进程阻塞于select调用,等待数据报套接字变为可读。当select返回套接字可读条件,进程调用recvfrom把所读数据报复制到应用程序缓冲区。
(4)信号驱动I/O(sign-driven I/O)
首先开启套接字的信号驱动I/O功能,并通过sigaction系统调用安装一个信号处理函数。该系统调用将立即返回,进程不被阻塞,继续执行。当数据报准备好读取时,内核就为进程产生一个SIGIO信号。进程既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已准备好待处理,也可以通知主循环,让他读取数据报。
(5)异步I/O(asynchornous I/O)
I/O多路复用机制
I/O多路复用就是通过一种机制,可以同时监视多个文件描述符,一旦某个描述符就绪,通知应用程序进行相应的操作。
系统调用 | select | poll | epoll |
工作原理 | 使用 fd_set 数组,每个数组元素表示一个文件描述符的状态,由内核根据I/O事件修改描述符状态,通知应用程序事件就绪。每次需要遍历所有的描述符。 | 与 select 类似,使用的是链表。 | 将就绪的I/O添加到就绪链表,然后轮询就绪的描述符。 |
最大文件描述符数目 | 一般有最大值限制(默认1024) | 系统允许打开的最大文件描述符数目(65535) | 系统允许打开的最大文件描述符数目(65535) |
工作模式 | LT | LT | LT/ET |
时间复杂度 | O(n) | O(n) | O(1) |
内核检测就绪事件 | 轮询 | 轮询 | 回调 |