Unix中5种IO模型
就网络通信而言,一次数据读入可以分为两个阶段,首先等待数据从网络中到达,到达后需要复制到内核的缓冲区中,第二个阶段是从内核的缓冲区复制到进程的缓冲区,复制到进程的缓冲区才算读取完毕。推广到所有IO,一次完整的IO读入分为两个阶段
- 等待数据准备完毕
- 把准备好的数据读到进程的内存空间
各种IO模型的区别在于当一个进程读取的数据没有准备好的时候该进程如何应对。
阻塞式IO
等待数据准备阶段,进程会一直阻塞,直到数据准备好或者抛出异常。调用recvfrom默认是阻塞式调用,当fd对应的数据没有准备好recvfrom会一直阻塞。
非阻塞式IO
recvfrom的非阻塞式调用,recvfrom对应的fd如果没有准备好数据,会返回EWOULDBLOCK,代表数据没有准备好,请求数据的进程不会阻塞而是立即返回。代表数据没有准备好。
IO复用模型
如果一个进程同时对多个fd感兴趣,可以把多个fd注册到select方法上,当注册的fd有数据准备好select会通知调用线程。广义而言这是一个阻塞模型,不同于阻塞IO,IO复用模型阻塞在select上,阻塞式IO阻塞在recvfrom上。
select的缺点在于如果轮询的文件描述符不够多,调用select的代价大于直接调用recvfrom的代价。且select能够同时监听的文件描述符的数量受限于内核的参数。
信号驱动IO模型
当数据没有准备好的时候,调用进程直接返回。等数据准备好的时候通过一个信号通知调用进程。这种模型应该也是一种非阻塞的模型,毕竟在没有读到数据的时候进程没有阻塞。区别于上述非阻塞模型,这种模型只需要询问一次数据是否准备好,然后只需要等待信号通知而后再次调用recvfrom,而上述非阻塞模型需要在没有获得数据的时候一直循环读数据。
异步IO
比信号驱动模型更高级,信号驱动模型中,进程在收到通知的是可以开始一个IO操作,异步IO模型中收到的通知是数据以及读取完毕。
- 只有最后一种IO模型是异步的,前四种模型在真正的IO操作中会阻塞进程,异步IO收到信号后数据以及读到进程缓冲区了
- 同步IO指在IO请求过程中进程阻塞,这个IO请求指的是在数据以及准备好了后真正执行recvfrom
IO多路复用
早期的IO多路复用技术采用select系统调用,由于select的一些固有缺陷如能够监听的最大文件数受限,后来采用epoll。其优点在于
- 注册的fd不受限制,select默认最大能够注册的文件描述符为1024个
- 效率不会因注册fd增多而降低。select会线性的扫描所有注册的fd,当注册的fd过一次线性扫描的时间会增加。epoll是一种“伪异步”,epoll依据fd上回调函数来实现。