解析Select、Poll、Epoll原理

文章目录

  • 1、SELECT
  • 2、Poll
  • 3、Epoll

每段代码都分为两个部分,准备数据和读取数据(以while(1)为分界线),本文主要研究select、poll和epoll是如何读取数据的。

1、SELECT

解析Select、Poll、Epoll原理_第1张图片

  • 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

解析Select、Poll、Epoll原理_第2张图片
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

解析Select、Poll、Epoll原理_第3张图片

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

你可能感兴趣的:(IO,epoll,select,poll)