文章目录
- 1、SELECT
- 2、Poll
- 3、Epoll
每段代码都分为两个部分,准备数据和读取数据(以while(1)为分界线),本文主要研究select、poll和epoll是如何读取数据的。
1、SELECT
- fds[i]:文件描述符,用于记录socket连接的位置,代码中可以接受五个socket的链接,max记录最大的链接编号。
- &rset:bitmap,1024位,用来表征哪一个文件描述符是被启用的,例如上面fds[i]={1,2,3,5,8},那么rset:01110100100000…共1024位
- Select:阻塞,将用户态的rset拷贝到内核态,由内核来判断是否有数据传来。没有则一直阻塞,有的话内核做两件事:①将rset有数据来的那一位置位②select函数返回
- 再次遍历fds[]数组,找到有数据的那个位置,将数据读出并处理
- 返回while(1)开头,重新将rset恢复成最初记录fds[]位置的状态
Select存在四个问题:
1、bitmap1024位,有上限
2、rset每次开头都要重新置位,rset不可重用
3、将rset拷贝到内核需要开销
4、Select返回后,还需要再次遍历所有fds
2、Poll
Pollfd结构体:
- fd:记录文件描述符,相当于select中的fds[]数组
- Events:记录该连接所关心的事件,有读、写、读写三种
- Revents:对所关心事件的回馈
Poll(pollfds,5,50000)
- pollfds: 记录pollfd的数组
- 5: 数组中有五个这样的元素
- 50000: 超时时间
该函数也是阻塞的,也需要将pollfds拷贝到内核,来让内核进行监听,没有数据时便一直阻塞,有数据时,将对应的pollfd的revents置位(比如pollin),然后poll函数返回。
检查每个pollfds[i]的revents,当与pollfds[i]的events相同时,表示有数据,进行读取和操作,并将revents重新置为0.
Poll解决了select的缺点①②。
3、Epoll
Epfd:
记录fd-events的集合(包含多个fd-events)
Epoll_wait():
用户态和内核态共享内存epfd,当有数据的时候,将有数据的fd放在前面,并返回有数据的fd个数。
LT:水平触发
当 epoll_wait() 检测到描述符事件到达时,将此事件通知进程,进程可以不立即处理该事件,下次调用 epoll_wait()会再次通知进程。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。
ET:边缘触发
和 LT 模式不同的是,通知之后进程必须立即处理事件。 下次再调用 epoll_wait() 时不会再得到事件到达的通知。很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
这样在之后读数据的时候就不用了全部fds循环一遍,只读取有数据的fds就可以了。
解决了③④问题。
视频链接:https://www.bilibili.com/video/BV1qJ411w7du