本小结源于B站freecoder同学的慷慨分享, 连接地址:
freecoder-IO多路复用select/poll/epoll介绍
1. select
1.1 关键词描述
fds=(文件描述符集合)监听的文件描述符的数组
max=监听的fds中->最大的文件描述符值
fdset/rset=bitmap[1024]=select函数的入参rset
select函数= select(max+1, &rset, &wset, &eset, timeout )
rset=读fds; wset=写fds; eset=异常的fds,
1.2 select存在的4个问题(缺点):
1. select的rset的1024个fd的限制;
bitmap默认大小: 1024个位, 只能处理1024个fds
2. select的rset不可重用;
fdset不可重用->每次都要赋值一个新的(传参rset);
3. select的rset拷贝:用户态<–>内核态的切换开销;
4. select的rset在内核置位后返回, 检查哪些fd置1, 仍需要O(n)遍历;
2. poll
poll引入了pollfd结构体(fd+events+revents)解决了select的前两个问题
poll函数=poll()
2.1 没有最大文件描述符数量的限制
不使用固定的1024大小的bitmap数组, 而是使用结构体数组;
2.2 pollfds结构体数组只改变revents的状态, 结构体可重用
比select优秀的是, select的fdset/rset不可重用; pollfds可以重用, 只是有数据准备好可读时, 将置位revents字段为POLLIN
2.3 问题3-用户态-内核态的切换仍存在, 没有解决;
2.4 问题4-返回后仍需要遍历结构体数组, O(n)问题仍存在;
3. epoll
epoll解决了select的四个问题;
后两个问题:
3.1 epfd是用户态和内核态共享的, 不存在赋值的问题
3.2 数据在内核标识置位后, 增加了重排操作, ready的fd都会排前,epoll返回时会返回ready的size;
4. epoll的扩展
4.1 epoll函数
4.1.1 epoll_create: 创建一个白板 存放fd_events
4.1.2 epoll_ctl: 向内核注册新的fd或改变某个fd的状态
已注册的fd在内核中会被维护在一棵红黑树上
4.1.3 epoll_wait: 进程调用 epoll_wait() 得到事件完成的fd
通过回调函数内核会将 I/O 准备好的fd加入到一个链表中管理,进程调用 epoll_wait() 便可以得到事件完成的描述符
4.2 epoll的2种工作模式
4.2.1 LT=level trigger=水平触发
当epoll_wait检测到fd事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。
下次调用epoll_wait时,会再次响应应用程序并通知此事件。
是默认的一种模式,并且同时支持 Blocking 和 No-Blocking
4.2.2. ET=edge trigger=边缘触发
当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。
如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。
LT 模式不同的是,通知之后进程必须立即处理事件。下次再调用 epoll_wait() 时不会再得到事件到达的通知。
ET: 很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。
只支持 No-Blocking,必须使用非阻塞套接口, 以避免由于一个fd的阻塞读/阻塞写操作把处理多个fd的任务饿死。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 hi@niewj.com