北飞

非一般的感觉
随笔 - 9, 文章 - 3, 评论 - 0, 引用 - 0
数据加载中……

另一个角度看epoll/select

零. 引言
      五一的时候闲暇下来看UNIX编程艺术,其实以前看过几次,这种书每次看都有新的收获,推荐大家常看。
      在前一篇文章中讲到了基础的网络知识,这一篇从其中抽取一个I/O多路复用这一点进行展开,期望能说出一些不太一样的东西,大家能够共同提高。select和epoll是常见的I/O多路复用的方式,对于两种的特点和性能差异,大家在网络上一搜索会好多的文件来介绍两者的原因,这里就不摆弄了,我这里想从另一个角度剖析这他们,期望从其中能得到更深层的收获,跟着linux系统学习设计。
一. select
先看select提供给用户的基本的api:
      FD_ZERO(fd_set *fdset);
      FD_SET(int fd, fd_set *fdset)
      FD_CLR(int fd, fd_set *fdset) 
      FD_ISSET(int fd, fd_set *fdset)  
      int select (int maxfd + 1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval * timeout);
      以上的api提供了套接字集合的操作函数和一个select,用户需要传递读集合,写集合和异常集合给系统,这就意味着这三个集合的维护的工作也需要交给用户(好重的负担),从这些信息中我们大致推测一些select的实现(仅仅是推测,没有探究源码):
     1. 用户维护感兴趣的读集合,写集合和异常集合
     2. 用户传递这些集合到内核中,内核需要扫描集合中所有的fd,判断是否可触发
     3. 返回给用户这些集合
      从这个流程上看,有大片的功能是由用户来维护的,提供给内核操作的空间很小,内核基本没有多大的可优化的地方,只能顺序扫描判断触发;但是真正了解套接字是否可以触发的是内核,但是内核在套接字触发时没有掌握足够的信息什么也做不了,用户的一次调用需要同步等待所有的fd都扫描完毕,但实际上很可能大部分的fd是不可触发的,可触发的时间分布也是不集中的。从某种角度上说,用户和内核两方面都挺蛋疼的。
      这里引出了一个非常核心的点,责任切分的问题,实现某一项功能需要多个模块的参与,哪个模块需要实现那些功能和接口,责任如何分割,这个是一个需要细细思考和锤炼的过程。
二. epoll
       说完select,再说一下epoll,epoll之所有性能强大,那是因为epoll采用了另外的思路的来实现I/O多路复用,先看一下epoll提供的api:
       int epoll_create(int size)
       int epoll_ctl(int epfd, int op, int fd, struct epoll_event event)
      int epoll_wait(int epfd,struct epoll_event   events,int maxevents,int timeout)
从API级别看的话,大致的流程是:
     1. 用户调用epoll_create创建一个指定大小的某个东西
     2. 用户调用epoll_ctrl针对某个套接字操作感兴趣的事件
     3. 调用epoll_wait等待已经触发的套接字
从大致的流程上来说看,select原先需要用户维护的信息已经交给内核了,内核完全可以在提供内部的机制和优化措施在套接字触发的时候做一些事情,当用户调用epoll_wait时直接获取已经触发的套接字即可
三. 小结
      两种系统调用在设计上采用了不同的思路导致了不同的性能境地,合适的操作丢给合适的模块实现会收到可观的效果;大家可以想想平时的工作中有没有类似的场景,学习之路漫长。

posted on 2014-09-27 21:05 北飞 阅读(84) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理