epoll和select区别

    原文出处:http://blog.csdn.net/ysu108/article/details/7570571 linux


    先说下本文框架,先是问题引出,而后归纳两个机制的区别和联系,最后介绍每一个接口的用法编程

1、问题引出 联系区别数组

问题的引出,当须要读两个以上的I/O的时候,若是使用阻塞式的I/O,那么可能长时间的阻塞在一个描述符上面,另外的描述符虽然有数据可是不能读出来,这样实时性不能知足要求,大概的解决方案有如下几种:服务器

1.使用多进程或者多线程,可是这种方法会形成程序的复杂,并且对与进程与线程的建立维护也须要不少的开销。(Apache服务器是用的子进程的方式,优势能够隔离用户)数据结构

2.用一个进程,可是使用非阻塞的I/O读取数据,当一个I/O不可读的时候马上返回,检查下一个是否可读,这种形式的循环为轮询(polling),这种方法比较浪费CPU时间,由于大多数时间是不可读,可是仍花费时间不断反复执行read系统调用。多线程

3.异步I/O(asynchronous I/O),当一个描述符准备好的时候用一个信号告诉进程,可是因为信号个数有限,多个描述符时不适用。框架

4.一种较好的方式为I/O多路转接(I/O multiplexing)(貌似也翻译多路复用),先构造一张有关描述符的列表(epoll中为队列),而后调用一个函数,直到这些描述符中的一个准备好时才返回,返回时告诉进程哪些I/O就绪。select和epoll这两个机制都是多路I/O机制的解决方案,select为POSIX标准中的,而epoll为Linux所特有的。异步

区别(epoll相对select优势)主要有三:socket

1.select的句柄数目受限,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE    1024  表示select最多同时监听1024个fd。而epoll没有,它的限制是最大的打开文件句柄数目。async

2.epoll的最大好处是不会随着FD的数目增加而下降效率,在selec中采用轮询处理,其中的数据结构相似一个数组的数据结构,而epoll是维护一个队列,直接看队列是否是空就能够了。epoll只会对"活跃"的socket进行操做---这是由于在内核实现中epoll是根据每一个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数(把这个句柄加入队列),其余idle状态句柄则不会,在这点上,epoll实现了一个"伪"AIO。可是若是绝大部分的I/O都是“活跃的”,每一个I/O端口使用率很高的话,epoll效率不必定比select高(多是要维护队列复杂)。

3.使用mmap加速内核与用户空间的消息传递。不管是select,poll仍是epoll都须要内核把FD消息通知给用户空间,如何避免没必要要的内存拷贝就很重要,在这点上,epoll是经过内核于用户空间mmap同一块内存实现的。

2、接口

1)select

1. int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr);

struct timeval{

  long tv_sec;

  long tv_usec;

}

有三种状况:tvptr == NULL 永远等待;tvptr->tv_sec == 0 && tvptr->tv_usec == 0 彻底不等待;不等于0的时候为等待的时间。select的三个指针均可觉得空,这时候select提供了一种比sleep更精确的定时器。注意select的第一个参数maxfdp1并非描述符的个数,而是最大的描述符加1,一是起限制做用,防止出错,二来能够给内核轮询的时候提供一个上届,提升效率。select返回-1表示出错,0表示超时,返回正值是全部的已经准备好的描述符个数(同一个描述符若是读和写都准备好,对结果影响是+2)。

2.int FD_ISSET(int fd, fd_set *fdset);  fd在描述符集合中非0,不然返回0

3.int FD_CLR(int fd, fd_set *fd_set); int FD_SET(int fd, fd_set *fdset) ;int FD_ZERO(fd_set *fdset);

用一段linux 中man里的话“FD_ZERO()  clears  a set.FD_SET() and  FD_CLR() respectively add and remove a given file descriptor from a set.  FD_ISSET() tests to see if a file descriptor is part of the set; this is useful after select() returns.”这几个函数与描述符的0和1不要紧,只是添加删除检测描述符是否在set中。

2)epoll

1.int epoll_create(int size);
建立一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不一样于select()中的第一个参数,给出最大监听的fd+1的值。须要注意的是,当建立好epoll句柄后,它就是会占用一个fd值,在linux下若是查看/proc/进程id/fd/,是可以看到这个fd的,因此在使用完epoll后,必须调用close()关闭,不然可能致使fd被耗尽。

2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不一样与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动做,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是须要监听的fd,第四个参数是告诉内核须要监听什么事,struct epoll_event结构以下:
struct epoll_event {
  __uint32_t events;  /* Epoll events */
  epoll_data_t data;  /* User data variable */
};

events能够是如下几个宏的集合:
EPOLLIN :表示对应的文件描述符能够读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符能够写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来讲的。
EPOLLONESHOT:只监听一次事件,当监听完此次事件以后,若是还须要继续监听这个socket的话,须要再次把这个socket加入到EPOLL队列里

关于epoll工做模式ET,LT

LT(level triggered)是缺省的工做方式,而且同时支持block和no-block socket.在这种作法中,内核告诉你一个文件描述符是否就绪了,而后你能够对这个就绪的fd进行IO操做。若是你不做任何操做,内核仍是会继续通知你的,因此,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的表明.
ET (edge-triggered)是高速工做方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核经过epoll告诉你。而后它会假设你知道文件描述符已经就绪,而且不会再为那个文件描述符发送更多的就绪通知,直到你作了某些操做致使那个文件描述符再也不为就绪状态了,可是请注意,若是一直不对这个fd做IO操做(从而致使它再次变成未就绪),内核不会发送更多的通知(only once)

3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)等待事件的产生,相似于select()调用。参数events用来从内核获得事件的集合,maxevents告以内核这个events有多大,这个maxevents的值不能大于建立epoll_create()时的size,参数timeout是超时时间(毫秒,0会当即返回,-1永久阻塞)。该函数返回须要处理的事件数目,如返回0表示已超时。

相关文章
相关标签/搜索