对于一次IO访问,以read为例,数据会先被拷贝到操作系统的缓冲区,然后从操作系统的缓冲区拷贝到用户的地址空间。read操作会经历下面两个阶段:
第一阶段:等待数据准备就绪
第二阶段:将数据从内核拷贝到进程中(内核态到用户态)
阻塞I/O模型
所有操作是阻塞的,直到数据包到达且被复制到应用进程的缓冲区中或者发生错误返回。以recvfrom(经过socket接收数据)调用举例。
非阻塞I/O模型
recvfrom调用如果缓冲区没有数据的话直接返回EWOULDBLOCK错误,一般对非阻塞I/O模型进行轮训检查这个状态看内核是否有数据到来。
I/O复用模型
Linux提供select/poll,进程可以将一个或多个fd(文件描述符)传递给select或poll调用,阻塞在select上,select负责侦测fd是否处于就绪状态。
多路复用技术通过把多个I/O的阻塞复用到一个select的阻塞上,从而使系统在单线程的情况下处理多客户端请求,最大的优势是系统开销小。
Linux的多路复用系统调用有:select、pselect、poll、epoll。
select固有的缺陷:
* 打开的fd数量受限(受限于FD_SIZE,修改需要重新编译内核,会导致性能下降)
* 采用轮训的方式,随着fd的增加性能线性下降
epoll的优化:
* 打开fd仅受限于操作系统的最大文件句柄数
* 由每个fd上的callback来实现对活跃的socket进行操作,避免了轮训的问题
* 采用mmap加速内核与用户控件的数据传递(共享内存)
* API更加简单
信号驱动I/O模型
开启套接口信号驱动I/O功能,并通过系统调用sigaction执行信号处理(非阻塞)。当数据准备就绪是,就会该进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据。
异步I/O
告知内核启动某个操作,并让内核在整个操作完成之后(包括数据从内核到用户空间的复制)通知应用程序。
和信号驱动模型的区别是:信号驱动提供信号通知应用程序何时可以开始IO操作;异步IO由直接通知何时完成了IO操作。