一个输入操作包括两个阶段:

  1. 等待数据从网络中到达,到达时被放在内核中的某个缓冲区中
  2. 把数据从内核缓冲区复制到应用进程缓冲区

    I/O模型

    同步IO:阻塞式、非阻塞式、IO复用(事件驱动)、信号驱动式
    异步IO

IO复用是使用select或者poll等待多个套接字中的任何一个变为可读,可以让单个进程具有处理多个 I/O 事件的能力
信号驱动和异步IO的区别:异步IO的信号是通知应用进程IO完成,信号驱动式是通知数据到了可以开始IO了
image.png
同步IO的区别就在第一阶段,第二阶段都是一样阻塞

I/O复用

select/poll/epoll 都是 I/O 多路复用的具体实现

select和poll

都是等待一组描述符中的一个成为就绪状态

二者比较:

  1. 功能:

基本相同,一些细节不同


select poll
修改描述符 不会
描述符数量限制 描述符类型由数组实现,默认1024 无数量限制
支持的事件类型和重复利用率
支持更多且重复利用率高
  1. 速度

都慢,每次调用都需要将全部描述符从应用进程缓冲区复制到内核缓冲区

  1. 可移植性

几乎所有的系统都支持 select,但是只有比较新的系统支持 poll。

epoll

  1. int epoll_create(int size);
  2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

epoll_ctl() 用于向内核注册新的描述符或者更新状态,已注册的描述符维护在红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理,进程调用 epoll_wait() 便可以得到事件完成的描述符。
epoll 只需要将描述符从进程缓冲区向内核缓冲区拷贝一次
仅使用与linux OS
无描述符数量限制
对多线程编程友好

工作模式:
水平触发与边缘触发

  1. LT 模式

当 epoll_wait() 检测到描述符事件到达时,将此事件通知进程,进程可以不立即处理该事件,下次调用 epoll_wait() 会再次通知进程。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。

  1. ET 模式

和 LT 模式不同的是,通知之后进程必须立即处理事件,下次再调用 epoll_wait() 时不会再得到事件到达的通知。
很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

应用场景

  1. select
    1. select 的 timeout 参数精度为微秒,而 poll 和 epoll 为毫秒,因此 select 更加适用于实时性要求比较高的场景,比如核反应堆的控制。
    2. 可移植性
  2. poll

无最大描述符数量限制,如果平台支持且实时性要求不高的话,优先级大于select

  1. epoll
    1. 要在linux平台,
    2. 有大量的描述符要同时轮询(1000个以上),
    3. 且最好是长连接:如果描述符状态变化多,而且都是非常短暂的,也没有必要使用 epoll,因为epoll将描述符都放在内核中,每次改变都要用系统调用 epoll_ctl()