epoll的ET和LT模式比较 - 源码分析
eventpoll是一种文件,它实现了一种机制利用一条rdllist队列来避免阻塞地进行poll。eventpoll归根到底还是在使用poll。而ET比LT高效,并不在于是否使用了poll,更不能说是因为LT使用了poll。通过阅读源代码就可以清楚看到对 ET 和 LT 处理的区别仅有一处,其余都相同。其实两者都在使用poll,只不过 ET 可避免多次在epoll_wait对不确定的rdllist进行重复poll检测。
首先来看sys_poll,f_op->poll 和 eventpoll 文件的关系。
sys_poll 可以通过wait机制,由被poll的文件的具体文件系统在文件发生状态变化时,通过wait设定的回调函数唤醒阻塞在poll的任务。
而eventpoll 则是不阻塞地使用wait机制,它使用ep_poll_callback作为wait的回调函数,让被poll的文件的具体文件系统在文件发生状态变化时,通过这个ep_poll_callback将自身关联epi链入到 eventpoll文件的 rdllist 中去。
对一个文件sys_poll时,会使用__poll_wait回调函数来阻塞等待事件唤醒。而将一个文件sys_epoll_ctl添加进eventpoll时,会使用ep_poll_callback实现异步的poll。
这里要区分sys_poll和file_operations->poll,阻塞是因为sys_poll执行poll_schedule_timeout(),而file_operations->poll只是建立wait机制的使用(,但是我们不可能绕开sys_poll直接去使用具体文件系统的poll)。所以eventpoll才可以有别于sys_poll进行异步的poll(,不去阻塞等待),也就是说sys_poll使用阻塞的政策,epoll_ctl的EPOLL_CTL_ADD使用异步的政策。实际两者同样都在使用wait机制的回调。
sys_epoll_wait只是关心eventpoll的rdllist队列是否为空,并且还必须对rdllist队列里面每一个关联的文件使用poll检测进行最终筛选。
而et 和 lvl-tri模式的唯一不同的处理则只是在sys_epoll_wait过程中。
调用路径为:
sys_epoll_wait() > ep_send_evnets() > ep_scan_ready_list() > ep_send_events_proc()
et 和 lvl-tri 模式的差异,仅仅是因为 ep_send_events_proc 一个小动作,影响了下一次的ep_scan_ready_list 处理。
下面是ep_scan_ready_list的处理流程,从流程清楚可以找出差异来。
0. ep_scan_ready_list 将rdllist 截出来收集到txlist
1. 当ep_scan_ready_list进行ep_send_events_proc时,ep_poll_callback (使用wait机制对文件进行poll的异步回调) 将epi (一个文件关系到eventpoll的结构)链入到 ovflist
2. 否则ep_poll_callback将epi 链入到 rdllist
3. ep_send_events_proc 将对txlist的每个epi的进行poll检测状态
如果满足状态
a. 发送到用户空间,
b. 并将 非EPOLLET的 epi 重新链入 rdllist
* 差异就在这里,对于LT模式下次还得通过poll进行筛选,即使你已经将文件的读缓冲读完了。
4. 在ep_scan_readly_list结束ep_send_events_proc后,会收集 ovflist 到 rdllist
5. 将未能写到用户空间的 txlist合并回rdllist
试想下面的情景:
当从epoll_wait取得事件后,同样都将读事件的文件的缓冲读完,并且没有写入发生时,再次进入epoll_wait。
在 et模式下,这个文件不会出现在eventpoll文件的rdllist中。
而 lvl-tri模式下,尽管这个文件已经不可能是POLLIN状态了,但下一次epoll_wait时,必须进行一次poll检测状态后从而筛选掉。
这个情景中,et模式比lvl-tri模式少了一次file_operations->poll检测,所以比较起来就有效得多了。
当有N个文件,M次读事件条件下,lvl-tri就可能会浪费N*M次poll检测。
但不论et模式还是lvl-tri模式,epoll_wait都必须对rdllist队列中每一个对应的文件使用poll检测进行筛选。
static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
void *priv)
{
struct ep_send_events_data *esed = priv;
int eventcnt;
unsigned int revents;
struct epitem *epi;
struct epoll_event __user *uevent;
struct wakeup_source *ws;
poll_table pt; init_poll_funcptr(&pt, NULL); /*
* We can loop without lock because we are passed a task private list.
* Items cannot vanish during the loop because ep_scan_ready_list() is
* holding "mtx" during this call.
*/
for (eventcnt = , uevent = esed->events;
!list_empty(head) && eventcnt < esed->maxevents;) {
epi = list_first_entry(head, struct epitem, rdllink); /*
* Activate ep->ws before deactivating epi->ws to prevent
* triggering auto-suspend here (in case we reactive epi->ws
* below).
*
* This could be rearranged to delay the deactivation of epi->ws
* instead, but then epi->ws would temporarily be out of sync
* with ep_is_linked().
*/
ws = ep_wakeup_source(epi);
if (ws) {
if (ws->active)
__pm_stay_awake(ep->ws);
__pm_relax(ws);
} list_del_init(&epi->rdllink); revents = ep_item_poll(epi, &pt); // 调用f_op->poll,但不是sys_poll /*
* If the event mask intersect the caller-requested one,
* deliver the event to userspace. Again, ep_scan_ready_list()
* is holding "mtx", so no operations coming from userspace
* can change the item.
*/
if (revents) {
if (__put_user(revents, &uevent->events) ||
__put_user(epi->event.data, &uevent->data)) {
list_add(&epi->rdllink, head);
ep_pm_stay_awake(epi);
return eventcnt ? eventcnt : -EFAULT;
}
eventcnt++;
uevent++;
if (epi->event.events & EPOLLONESHOT)
epi->event.events &= EP_PRIVATE_BITS;
else if (!(epi->event.events & EPOLLET)) {
/*
* If this file has been added with Level
* Trigger mode, we need to insert back inside
* the ready list, so that the next call to
* epoll_wait() will check again the events
* availability. At this point, no one can insert
* into ep->rdllist besides us. The epoll_ctl()
* callers are locked out by
* ep_scan_ready_list() holding "mtx" and the
* poll callback will queue them in ep->ovflist.
*/
list_add_tail(&epi->rdllink, &ep->rdllist);
ep_pm_stay_awake(epi);
}
}
} return eventcnt;
}
eventpoll是一个文件,同样可以使用sys_poll对其进行阻塞poll检测。
ep_scan_ready_list() 配合 ep_read_events_proc() 使用在eventpoll的file_operations->poll中。
(ep_scan_ready_list() 配合 ep_send_events_proc() 使用在epoll_wait)
用于eventpoll文件被poll
1. 检查rdllist中是否包含至少一个满足期望轮询到的状态。
2. 对rdllist中的每一个epi进行poll检测:
a. 满足就返回;
b. 不满足,顺便称出rdllist。
epoll的ET和LT模式比较 - 源码分析的更多相关文章
- Java的三种代理模式&完整源码分析
Java的三种代理模式&完整源码分析 参考资料: 博客园-Java的三种代理模式 简书-JDK动态代理-超详细源码分析 [博客园-WeakCache缓存的实现机制](https://www.c ...
- Seata AT 模式启动源码分析
从上一篇文章「分布式事务中间件Seata的设计原理」讲了下 Seata AT 模式的一些设计原理,从中也知道了 AT 模式的三个角色(RM.TM.TC),接下来我会更新 Seata 源码分析系列文章. ...
- 设计模式(九)——装饰者模式(io源码分析)
1 星巴克咖啡订单项目(咖啡馆): 1) 咖啡种类/单品咖啡:Espresso(意大利浓咖啡).ShortBlack.LongBlack(美式咖啡).Decaf(无因咖啡) 2) 调料:Milk.So ...
- Future模式的学习以及JDK内置Future模式的源码分析
并发程序设计之Future模式 一).使用Future模式的原因 当某一段程序提交了一个请求,期待得到一个答复,但服务程序对这个请求的处理可能很慢,在单线程的环境中,调用函数是同步的,必须等到服务程序 ...
- 并发编程学习笔记(9)----AQS的共享模式源码分析及CountDownLatch使用及原理
1. AQS共享模式 前面已经说过了AQS的原理及独享模式的源码分析,今天就来学习共享模式下的AQS的几个接口的源码. 首先还是从顶级接口acquireShared()方法入手: public fin ...
- Seata源码分析(一). AT模式底层实现
目录 GlobalTransactionScanner 继承AbstractAutoProxyCreator 实现InitializingBean接口 写在最后 以AT为例,我们使用Seata时只需要 ...
- Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析
Java 读写锁 ReentrantReadWriteLock 源码分析 转自:https://www.javadoop.com/post/reentrant-read-write-lock#toc5 ...
- 源码分析:Semaphore之信号量
简介 Semaphore 又名计数信号量,从概念上来讲,信号量初始并维护一定数量的许可证,使用之前先要先获得一个许可,用完之后再释放一个许可.信号量通常用于限制线程的数量来控制访问某些资源,从而达到单 ...
- epoll源码分析
epoll源码分析 最近在使用libev过程中遇到一个场景:一个fd从一个ev_loop迁移到另一个ev_loop,会出现这个fd同时存在两个epoll的瞬间.不禁要问了,一个fd同时被两个epoll ...
随机推荐
- Java常用API解析——序列化API
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6797659.html 工作中发现,自己对Java的了解还很片面,没有深入的研究,有很多的J ...
- 从SQL Server数据库转到Oracle数据库的数据脚本处理
在我们很多情况下的开发,为了方便或者通用性的考虑,都首先考虑SQL Server数据库进行开发,但有时候客户的生产环境是Oracle或者其他数据库,那么我们就需要把对应的数据结构和数据脚本转换为对应的 ...
- IOS安装CocoaPods详情过程
一.简介 什么是CocoaPods CocoaPods是OS X和iOS下的一个第三类库管理工具,通过CocoaPods工具我们可以为项目添加被称为“Pods”的依赖库(这些类库必须是CocoaPod ...
- sublime Text2 快捷键精华版
Ctrl+Shift+P:打开命令面板Ctrl+P:搜索项目中的文件Ctrl+G:跳转到第几行Ctrl+W:关闭当前打开文件Ctrl+Shift+W:关闭所有打开文件Ctrl+Shift+V:粘贴 ...
- PHP导出生成CSV文件
composer 用起来是非常方便的 所以我是依赖composer来做的包管理 1.先安装composer 自行百度一下composer安装以及使用 2.用composer下载安装office包即可 ...
- Appium 1.6.4 环境搭建流程(Java, Android+IOS, Windows+Mac)
Appium1.6.4已经出来一段时间了,快速给大家串一下怎么搭建,贴了下载链接 1 基础环境: Windows + Mac: Java JDK 1.8+ (需配置环境变量),Appium1.6.4的 ...
- GD库知识点
GD库:PHP的一个扩展库,主要用于绘制动态图,根据数据动态响应的图片 如统计图 验证码 其他的用途如:处理已有图像 图片的缩放 裁剪 图片水印 文字水印 1.安装GD库 2.画图步骤:创建背景图像( ...
- 2017PHP程序员的进阶之路
2017PHP程序员的进阶之路 又是一年毕业季,可能会有好多毕业生即将进入开发这个圈子,踏上码农这个不归路.根据这些年在开发圈子总结的LNMP程序猿发展轨迹,结合个人经验体会,总结出很多程序员对未来的 ...
- 【转】HTTP长连接与短连接(2)
一.什么是长连接 HTTP1.1规定了默认保持长连接(HTTP persistent connection ,也有翻译为持久连接),数据传输完成了保持TCP连接不断开(不发RST包.不四次握手),等待 ...
- List在执行remove方法不能删除指定的对象
我们根据List中的源码分析, remove方法的原理: public boolean remove(Object o){ if(o ==null) { for(inti ...