另外,struct epoll event结构中的events域在这里的解释是:在被监测的文件描述符上实际发生的事件 。
参数timeout描述在函数调用中阻塞时间上限,单位是ms:
- timeout = -1表示调用将一直阻塞,直到有文件描述符进入ready状态或者捕获到信号才返回;
- timeout = 0用于非阻塞检测是否有描述符处于ready状态,不管结果怎么样,调用都立即返回;
- timeout > 0表示调用将最多持续timeout时间,如果期间有检测对象变为ready状态或者捕获到信号则返回,否则直到超时 。
epoll监控多个文件描述符的I/O事件 。epoll支持边缘触发(edge trigger,ET)或水平触发(level trigger,LT),通过epoll_wait等待I/O事件,如果当前没有可用的事件则阻塞调用线程 。
select和poll只支持LT工作模式,epoll的默认的工作模式是LT模式 。1.水平触发的时机
- 对于读操作,只要缓冲内容不为空,LT模式返回读就绪 。
- 对于写操作,只要缓冲区还不满,LT模式会返回写就绪 。
2.边缘触发的时机
- 对于读操作
- 当缓冲区由不可读变为可读的时候,即缓冲区由空变为不空的时候 。
- 当有新数据到达时,即缓冲区中的待读数据变多的时候 。
- 当缓冲区有数据可读,且应用进程对相应的描述符进行EPOLL_CTL_MOD 修改EPOLLIN事件时 。
- 对于写操作
- 当缓冲区由不可写变为可写时 。
- 当有旧数据被发送走,即缓冲区中的内容变少的时候 。
- 当缓冲区有空间可写,且应用进程对相应的描述符进行EPOLL_CTL_MOD 修改EPOLLOUT事件时 。
在ET模式下,缓冲区从不可读变成可读,会唤醒应用进程,缓冲区数据变少的情况,则不会再唤醒应用进程 。举例1:
- 读缓冲区刚开始是空的
- 读缓冲区写入2KB数据
- 水平触发和边缘触发模式此时都会发出可读信号
- 收到信号通知后,读取了1KB的数据,读缓冲区还剩余1KB数据
- 水平触发会再次进行通知,而边缘触发不会再进行通知
- 水平触发:0为无数据,1为有数据 。缓冲区有数据则一直为1,则一直触发 。
- 边缘触发发:0为无数据,1为有数据,只要在0变到1的上升沿才触发 。
JDK并没有实现边缘触发,Netty重新实现了epoll机制,采用边缘触发方式;另外像Nginx也采用边缘触发 。JDK在Linux已经默认使用epoll方式,但是JDK的epoll采用的是水平触发,而Netty重新实现了epoll机制,采用边缘触发方式,netty epoll transport 暴露了更多的nio没有的配置参数,如 TCP_CORK, SO_REUSEADDR等等;另外像Nginx也采用边缘触发 。
epoll与select、poll的对比1. 用户态将文件描述符传入内核的方式
- select:创建3个文件描述符集并拷贝到内核中,分别监听读、写、异常动作 。这里受到单个进程可以打开的fd数量限制,默认是1024 。
- poll:将传入的struct pollfd结构体数组拷贝到内核中进行监听 。
- epoll:执行epoll_create会在内核的高速cache区中建立一颗红黑树以及就绪链表(该链表存储已经就绪的文件描述符) 。接着用户执行的epoll_ctl函数添加文件描述符会在红黑树上增加相应的结点 。
- select:采用轮询方式,遍历所有fd,最后返回一个描述符读写操作是否就绪的mask掩码,根据这个掩码给fd_set赋值 。
- poll:同样采用轮询方式,查询每个fd的状态,如果就绪则在等待队列中加入一项并继续遍历 。
推荐阅读
- 如何彻底删除烦人的电脑弹窗广告?
- 800 字彻底理解 Go 指针
- 一文搞懂 Traefik2.1 的使用
- 5分钟!彻底搞懂MyBatis插件+PageHelper原理
- 彻底搞懂 Netty 线程模型
- 猫咪可以经常换猫砂吗 猫咪猫砂多久彻底换一次
- 如何彻底清理干净电脑里的垃圾?
- 一文搞懂分类算法中常用的评估指标
- 看了两天HashMap源码,终于把红黑树插入平衡规则搞懂了
- 三分钟搞懂SQL的Case函数
