一个输入操作包括两个阶段:
IO复用是使用select或者poll等待多个套接字中的任何一个变为可读,可以让单个进程具有处理多个 I/O 事件的能力
信号驱动和异步IO的区别:异步IO的信号是通知应用进程IO完成,信号驱动式是通知数据到了可以开始IO了
同步IO的区别就在第一阶段,第二阶段都是一样阻塞
I/O复用
select/poll/epoll 都是 I/O 多路复用的具体实现
select和poll
都是等待一组描述符中的一个成为就绪状态
二者比较:
- 功能:
基本相同,一些细节不同
select | poll | |
---|---|---|
修改描述符 | 会 | 不会 |
描述符数量限制 | 描述符类型由数组实现,默认1024 | 无数量限制 |
支持的事件类型和重复利用率 | 支持更多且重复利用率高 |
- 速度
都慢,每次调用都需要将全部描述符从应用进程缓冲区复制到内核缓冲区
- 可移植性
几乎所有的系统都支持 select,但是只有比较新的系统支持 poll。
epoll
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
epoll_ctl() 用于向内核注册新的描述符或者更新状态,已注册的描述符维护在红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理,进程调用 epoll_wait() 便可以得到事件完成的描述符。
epoll 只需要将描述符从进程缓冲区向内核缓冲区拷贝一次
仅使用与linux OS
无描述符数量限制
对多线程编程友好
工作模式:
水平触发与边缘触发
- LT 模式
当 epoll_wait() 检测到描述符事件到达时,将此事件通知进程,进程可以不立即处理该事件,下次调用 epoll_wait() 会再次通知进程。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。
- ET 模式
和 LT 模式不同的是,通知之后进程必须立即处理事件,下次再调用 epoll_wait() 时不会再得到事件到达的通知。
很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
应用场景
- select
- select 的 timeout 参数精度为微秒,而 poll 和 epoll 为毫秒,因此 select 更加适用于实时性要求比较高的场景,比如核反应堆的控制。
- 可移植性
- poll
无最大描述符数量限制,如果平台支持且实时性要求不高的话,优先级大于select
- epoll
- 要在linux平台,
- 有大量的描述符要同时轮询(1000个以上),
- 且最好是长连接:如果描述符状态变化多,而且都是非常短暂的,也没有必要使用 epoll,因为epoll将描述符都放在内核中,每次改变都要用系统调用 epoll_ctl()