epoll与select的区别

来源:互联网 发布:软件著作权申请表填写 编辑:程序博客网 时间:2024/06/11 18:29
epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现。

网上现在关于这两者不同的介绍已经到处都是了。我这里也不能多说出什么东西,只是记录下我看了实现代码之后的一些总结。

两者的使用场景一般是通过一个入口能够同时监控多路I/O。一般使用的接口,
epool就是
  1. int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
select为:
  1. int select(int nfds, fd_set *readfds, fd_set *writefdsfd_set *exceptfds, struct timeval *timeout);
通过上述两个函数能够将调用线程阻塞,线程变为可执行条件有两种情况:
  1. 无任何事件发生,超时时间已过
  2. 在所控制的I/O有事件到来
epoll_wait函数中看不到相关的监控信息,因为是通过epoll_ctl已经加入,而select之间在函数调用中由(fd_set *readfds, fd_set *writefdsfd_set *exceptfds)传入。epoll_wait饭互结果通过events返回,而select的传入参数也是传出参数。两者传出参数均表示发生事件的对应I/O标识。

两种方式的区别主要体现在以下几个方面:
  1. select所能控制的I/O数有限,这主要是因为fd_set数据结构是一个有大小的,相当与一个定长所数组。
  2. select每次都需要重新设置所要监控的fd_set(因为调用之后会改变其内容),这增加了程序开销。
  3. select的性能要比epoll差,具体原因会在后续内容中详细说明。
嗯,说道这个为什么select要差,那就要从这个select API说起了。这个传进去一个数组,内部实现也不知道那个有哪个没有,所以要遍历一遍。假设说我只监控一个文件描述符,但是他是1000。那么select需要遍历前999个之后再来poll这个1000的文件描述符,而epoll则不需要,因为在之前epoll_ctl的调用过程中,已经维护了一个队列,所以直接等待事件到来就可以了。
Linux中select此段相关代码为:
  1. /* 遍历所有传入的fd_set */
  2. for (= 0; i < n; ++rinp, ++routp, ++rexp) {
  3.     unsigned long in, out, ex, all_bits, bit = 1, mask, j;
  4.     unsigned long res_in = 0, res_out = 0, res_ex = 0; 
  5.     const struct file_operations *f_op = NULL;
  6.     struct file *file = NULL;

  7.     in = *inp++; out = *outp++; ex = *exp++;
  8.     all_bits = in | out | ex;
  9.     /* 此处跳无需监控的fd, 白白的浪费时间啊…… */        
  10.     if (all_bits == 0) { 
  11.         i += __NFDBITS;
  12.         continue;
  13.     } 
  14.     /* 后续进行一些相关操作 */
  15. }
而epoll则无需进行此类操作,直接检测内部维护的一个就绪队列,如果队列有内容,说明有I/O就绪,那么直接赋值返回内容,成功返回,如果没有成功,那么睡眠,等待就绪队列非空。

通过这个两者的比较,其实两者的差距啊,大部分是因为这个API设计所决定的,select就设计成这样一个API,内部再怎么优化也只能是这么个烂样子,而epoll这样维护与等待分离,灵活多变,最后也就带来了相对的高性能,以及可扩展性。

以后的代码生涯中,还是先要设计好API,然后才能写出好代码……
(转自:http://blog.chinaunix.net/uid-23146151-id-3112631.html)