Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2039003
  • 博文数量: 369
  • 博客积分: 10093
  • 博客等级: 上将
  • 技术积分: 4271
  • 用 户 组: 普通用户
  • 注册时间: 2005-03-21 00:59
文章分类

全部博文(369)

文章存档

2013年(1)

2011年(2)

2010年(10)

2009年(16)

2008年(33)

2007年(146)

2006年(160)

2005年(1)

分类: LINUX

2008-01-15 23:57:37

epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它不会复用文件描述符集合来传递结果而迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll的除了提供select/poll那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

epoll相关的系统调用有:epoll_create, epoll_ctl和epoll_wait。Linux-2.6.19又引入了可以屏蔽指定信号的epoll_wait: epoll_pwait。至此epoll家族已全。其中epoll_create用来创建一个epoll文件描述符,epoll_ctl用来添加/修改/删除需要侦听的文件描述符及其事件,epoll_wait/epoll_pwait接收发生在被侦听的描述符上的,用户感兴趣的IO事件。epoll文件描述符用完后,直接用close关闭即可,非常方便。事实上,任何被侦听的文件符只要其被关闭,那么它也会自动从被侦听的文件描述符集合中删除,很是智能。

每次添加/修改/删除被侦听文件描述符都需要调用epoll_ctl,所以要尽量少地调用epoll_ctl,防止其所引来的开销抵消其带来的好处。有的时候,应用中可能存在大量的短连接(比如说Web服务器),epoll_ctl将被频繁地调用,可能成为这个系统的瓶颈。

我查看了内核中的侦听描述符集合的组织形式,发现其所采用的数据结构是红黑树(rbtree),这种树的插入/删除/查找的平均时间复杂度都是O(log2(N)),可以说已经达到了“理论”上的极限。但是,数据结构的选择应该是因地制宜的,应该充分考虑到其所处环境:
  1. 他所操作的对象是文件描述符,文件描述符的一个重要的特性就是总分配最小可用的文件描述符,换句话说就是:他们一般密集地分部在数值较小的区间。
  2. 考虑到只有PIPE/Socket等管道类的文件描述符才能被侦听可读、可写等事件,一般的文件操作的异步事件都是读完、写完等,所以,实际上可侦听的文件描述符的密度可能只有50%(Web服务器),但还是有能接近100%的(代理网关),即使是50%,也依然很高。
所以,插入/删除/查找的时间复杂度都是O(1)的数据结构--数组应该更适合,为此,我专门给内核做了一个,用数组替代了以前的红黑树,并用一个简单的做了测试,结果显示epoll操作的时间复杂度确实从O(log2(N))降到了O(1),效果明显。

尝试着向官方内核补丁,不幸地,理由是“we aren't going back",因为红黑树就是从最初的哈希表来的,至于改的理由“might exhaust memory badly”,个人感觉有些牵强。

把这个补丁索引在这里了,希望可能对大家有用!
阅读(4445) | 评论(10) | 转发(0) |
1

上一篇:新年

下一篇:无题

给主人留下些什么吧!~~

xiaosuo2010-01-21 14:45:30

dup2(2)和dup3(2)可以指定所用的文件描述符,这可以在某种程度上降低对上述代码进行内存攻击的难度。权记之。

chinaunix网友2008-07-14 13:36:14

遍历处理是我想错了,看来主要的劣势就是内存使用,这个想法应该可以作为epoll增强接口供有兴趣者跑跑看.

xiaosuo2008-07-14 12:54:23

我这个也是可控的,最多不会多于最大文件描述符平方数个槽位,要想以此作为攻击的切入点不怎么容易。 epoll是在被唤醒的回掉函数中将fd加入ready队列,所以,你所担心的问题,实际上并不存在,也就是说epoll_wait没有必要遍历所有的事件,每次只读取那些处于ready队列中的fd就可以了,这也是相较于select/poll的重大改进。 至于具体的测试结果,我找不到了,你可以用我上面提到的程序再跑一下。

chinaunix网友2008-07-14 11:08:36

也说得过去,但fdtable是每进程一个,你这个是每epoll fd一个,前者属于可控的. 使用全数组,查找/插入/删除是快了,但遍历却慢了,没记错的话epoll_wait主要的操作就是遍历所有事件,现在遍历的下限变成了O(n),而且n=fdtable size,这也是select最受诟病的地方:无论发生几个事件,都要遍历整个数组,只不过select在用户态做,epoll在kernel中做. 我没有看你的代码,只是说说可能性,要是你有测试数据能说明就更好了:)

xiaosuo2008-07-13 00:50:29

你这个疑问也有人问过,这个设计实际上是参考了fdtable所采用的数据结构,如果fdtable都不用担心内存需求问题,为什么epollfd需要考虑呢?实际上,动态数组的内存消耗是有和fdtable相同的size上限的。