linux epoll总结

什么是epoll

epoll是什么?按照man手册的说法:是为处理大批量句柄而做了改进的poll。固然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in Linux kernel 2.5.44),它几乎具有了以前所说的一切优势,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。node

epoll的相关系统调用

epoll只有epoll_create,epoll_ctl,epoll_wait 3个系统调用。linux

1. int epoll_create(int size);数组

建立一个epoll的句柄。自从linux2.6.8以后,size参数是被忽略的。须要注意的是,当建立好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()的返回值。socket

第二个参数表示动做,用三个宏来表示:函数

EPOLL_CTL_ADD:注册新的fd到epfd中;性能

EPOLL_CTL_MOD:修改已经注册的fd的监听事件;ui

EPOLL_CTL_DEL:从epfd中删除一个fd;

 

第三个参数是须要监听的fd。

第四个参数是告诉内核须要监听什么事,struct epoll_event结构以下:

 

typedef union epoll_data {  
    void *ptr;  
    int fd;  
    __uint32_t u32;  
    __uint64_t u64;  
} epoll_data_t;  
 //感兴趣的事件和被触发的事件  
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队列里

 

 

3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

收集在epoll监控的事件中已经发送的事件。参数events是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不能够是空指针,内核只负责把数据复制到这个events数组中,不会去帮助咱们在用户态中分配内存)maxevents告以内核这个events有多大,这个 maxevents的值不能大于建立epoll_create()时的size,参数timeout是超时时间(毫秒,0会当即返回,-1是永久阻塞)。若是函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时。

 

Epoll的2种工做方式-水平触发(LT)和边缘触发(ET)

 水平触发(LT):缺省的工做方式,若是一个描述符就绪,内核就会通知处理,若是不进行处理,下一次内核仍是会通知

边缘触发(ET):只支持非阻塞描述符。须要程序保证缓存区的数据所有被读取或者所有写出(觉得ET模式下,描述符的就绪不会再次通知),所以须要发的非阻塞的描述符。

                              对于读操做,若是read一次没有读尽buffer中的数据,那么下次将得不到读就绪的通知,形成buffer中已有的数据无机会读出,除非有新的数据再次到达。对于写操做,主要是由于ET模式下fd一般为非阻塞形成的一个问题——如何保证                              将用户要求写的数据写完。

 

epoll相比于select/poll的优势:

1.支持一个进程打开大数目的socket描述符(FD)

    select 最不能忍受的是一个进程所打开的FD是有必定限制的,由FD_SETSIZE设置,默认值是2048。对于那些须要支持的上万链接数目的IM服务器来讲显然太少了。这时候你一是能够选择修改这个宏而后从新编译内核,不过资料也同时指出这样会带来网络效率的降低,二是能够选择多进程的解决方案(传统的 Apache方案),不过虽然linux上面建立进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,因此也不是一种完美的方案。不过 epoll则没有这个限制,它所支持的FD上限是最大能够打开文件的数目,这个数字通常远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目能够cat /proc/sys/fs/file-max察看,通常来讲这个数目和系统内存关系很大。

2.IO效率不随FD数目增长而线性降低

    传统的select/poll另外一个致命弱点就是当你拥有一个很大的socket集合,不过因为网络延时,任一时间只有部分的socket是"活跃"的,可是select/poll每次调用都会线性扫描所有的集合,致使效率呈现线性降低。可是epoll不存在这个问题,它只会对"活跃"的socket进行操做---这是由于在内核实现中epoll是根据每一个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数,其余idle状态socket则不会,在这点上,epoll实现了一个"伪"AIO,由于这时候推进力在os内核。在一些 benchmark中,若是全部的socket基本上都是活跃的---好比一个高速LAN环境,epoll并不比select/poll有什么效率,相反,若是过多使用epoll_ctl,效率相比还有稍微的降低。可是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。

3.使用mmap加速内核与用户空间的消息传递

    不管是select,poll仍是epoll都须要内核把FD消息通知给用户空间,如何避免没必要要的内存拷贝就很重要,在这点上,epoll是经过内核于用户空间mmap同一块内存实现的。而若是你想我同样从2.5内核就关注epoll的话,必定不会忘记手工 mmap这一步的。

 

epoll机理

当某一进程调用epoll_create方法时,Linux内核会建立一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。eventpoll结构体以下所示:

struct eventpoll{  
    ....  
    /*红黑树的根节点,这颗树中存储着全部添加到epoll中的须要监控的事件*/  
    struct rb_root  rbr;  
    /*双链表中则存放着将要经过epoll_wait返回给用户的知足条件的事件*/  
    struct list_head rdlist;  
    ....  
};  

每个epoll对象都有一个独立的eventpoll结构体,用于存放经过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就能够经过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。

而全部添加到epoll中的事件都会与设备(网卡)驱动程序创建回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。

在epoll中,对于每个事件,都会创建一个epitem结构体,以下所示:

struct epitem{  
    struct rb_node  rbn;//红黑树节点  
    struct list_head    rdllink;//双向链表节点  
    struct epoll_filefd  ffd;  //事件句柄信息  
    struct eventpoll *ep;    //指向其所属的eventpoll对象  
    struct epoll_event event; //期待发生的事件类型  
}

当调用epoll_wait检查是否有事件发生时,只须要检查eventpoll对象中的rdlist双链表中是否有epitem元素便可。若是rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。

相关文章
相关标签/搜索