epoll详解

在linux 没有实现epoll事件驱动机制之前,我们一般选择用select或者poll等IO多路复用的方法来实现并发服务程序。自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术,在大数据、高并发、集群等一些名词唱得火热之年代,select和poll的用武之地越来越有限,风头已经被epoll占尽。

那究竟什么是epoll呢?epoll 是 Linux 内核的可扩展 I/O 事件通知机制,其最大的特点就是性能优异。

打个比方说说一下select,poll,epoll的区别:select就像一个兵长,每一个小兵路过他都要问一句“有没有事件发生啊?”,一个兵长的能力有限,他一次最多可以问1024个小兵;而poll这个兵长能力比select强一些,他不限制询问小兵的数量,也就是没有1024这个限制了。而epoll这个兵长就更厉害了更聪明了,他不仅不限制数量还不用一个一个亲自去问,他只需要坐在办公室喝茶了,因为他给每个小兵都写了一个名字在脸上,只要这个小兵来了,epoll兵长只要看一眼就知道发生什么事件了,然后再通知上级。

也就是说,epoll在获取事件的时候,无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。

epoll除了提供select/poll那种IO事 件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减 少epoll_wait/epoll_pwait的调用,提高应用程序效率。

水平触发:

是缺省的工作方式,什么叫缺省呢?就是当一个文件描述符准备就绪了,内核就会通知你可以对这个就绪的fd进行IO操作,如果你一直不操作内核就会一直提醒你直到你对这个fd进行处理。这种方式的好处就是可以保证出错的概率小一些,传统的select和poll都是采用的这种工作方式。

边沿触发:

是高速工作方式,在这种模式下,当描述符从未就绪变为就绪时,内核通 过epoll告诉你。然后内核默认你已经知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了

LT模式只要有事件未处理就会触发,而ET则只在高低电平变换时(即状态从1到0或者0到1)触发。

epoll的实现分为三步:

1.调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
2. 调用epoll_ctl()向epoll对象中添加连接的套接字
3. 调用epoll_wait()收集发生的事件的连接
接下来详细说一下这三个函数:

1.创建epoll实例:int epoll_create();

#include <sys/epoll.h>
int epoll_create(int size);
描述:系统调用epoll_create()创建了一个新的epoll实例,其对应的兴趣列表初始化为空。当这个文件描述符不再需要时,应该通过close()来关闭。
参数size:指定了我们想要通过epoll实例来检查的文件描述符个数。
该参数并不是一个上限,而是告诉内核应该如何为内部数据结构划分初始大小。(从Linux2.6.8版以来,size参数被忽略不用)
返回值: 若成功返回文件描述符,若出错返回-1。

2.修改epoll的兴趣列表:epoll_ctl()

#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev);
描述:系统调用epoll_ctl()能够修改由文件描述符epfd所代表的epoll实例中的兴趣列表。若成功返回0,若出错返回-1。
第一个参数epfd:是epoll_create()的返回值;
第二个参数op:用来指定需要执行的操作,它可以是如下几种值:
EPOLL_CTL_ADD:将描述符fd添加到epoll实例中的兴趣列表中去。对于fd上我们感兴趣的事件,都指定在ev所指向的结构体中。如果我们试图向兴趣列表中添加一个已存在的文件描述,epoll_ctl()将出现EEXIST错误;
EPOLL_CTL_MOD:修改描述符上设定的事件,需要用到由ev所指向的结构体中的信息。如果我们试图修改不在兴趣列表中的文件描述符,epoll_ctl()将出现ENOENT错误;
EPOLL_CTL_DEL:将文件描述符fd从epfd的兴趣列表中移除,该操作忽略参数ev。如果我们试图移除一个不在epfd的兴 趣列表中的文件描述符,epoll_ctl()将出现ENOENT错误。关闭一个文件描述符会自动将其从所有的epoll实例的兴趣列表移除;
第三个参数fd:指明了要修改兴趣列表中的哪一个文件描述符的设定。
该参数可以是代表管道、FIFO、套接字、POSIX消息队列、inotify实例、终端、设备,甚至是另一个epoll实例的文件描述符。但是,这里fd不能作为普通文件或目录的文件描述符;
第四个参数ev是指向结构体epoll_event的指针,结构体的定义如下:
typedef union epoll_data
{
 void *ptr; /* Pointer to user-defind data */
 int fd; /* File descriptor */
 uint32_t u32; /* 32-bit integer */
 uint64_t u64; /* 64-bit integer */
} epoll_data_t;
struct epoll_event
{
 uint32_t events; /* epoll events(bit mask) */
 epoll_data_t data; /* User data */
};
参数ev为文件描述符fd所做的设置(epoll_event)如下:
data字段是一个联合体,当描述符fd稍后称为就绪态时,联合的成员可用来指定传回给调用进程的信息;
events字段是一个位掩码,它指定了我们为待检查的描述符fd上所感兴趣的事件集合;

3.事件等待:epoll_wait()

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout);

描述:系统调用epoll_wait()返回epoll实例中处于就绪态的文件描述符信息,单个epoll_wait()调用能够返回多个就绪态文件描述符的信息。调用成功后epoll_wait()返回数组evlist中的元素个数,如果在timeout超时间隔内没有任何文件描述符处于就绪态的话就返回0,出错时返回-1并在errno中设定错误码以表示错误原因。

第一个参数epfd是epoll_create()的返回值;
第二个参数evlist所指向的结构体数组中返回的是有关就绪态文件描述符的信息,数组evlist的空间由调用者负责申请;
第三个参数maxevents指定所evlist数组里包含的元素个数;
第四个参数timeout用来确定epoll_wait()的阻塞行为,有如下几种:
如果timeout等于-1,调用将一直阻塞,直到兴趣列表中的文件描述符上有事件产生或者直到捕获到一个信号为止。
如果timeout等于0,执行一次非阻塞式地检查,看兴趣列表中的描述符上产生了哪个事件。
如果timeout大于0,调用将阻塞至多timeout毫秒,直到文件描述符上有事件发生,或者直到捕获到一个信号为止。
数组evlist中,每个元素返回的都是单个就绪态文件描述符的信息。events字段返回了在该描述符上已经发生的事件掩码。data字段返回的是我们在描述符上使用epoll_ctl()注册感兴趣的事件时在ev.data中所指定的值。注意,data字段是唯一可获知同这个事件相关的文件描述符的途径。因此,当我们调用epoll_ctl()将文件描述符添加到感兴趣列表中时,应该要么将ev.date.fd设为文件描述符号,要么将ev.date.ptr设为指向包含文件描述符号的结构体。
当我们调用epoll_ctl()时可以在ev.events中指定的位掩码以及由epoll_wait()返回的evlist[].events中的值如下所示:

 

默认情况下,一旦通过epoll_ctl()的EPOLL_CTL_ADD操作将文件描述符添加到epoll实例的兴趣列表中后,它会保持激活状态(即,之后对epoll_wait()的调用会在描述符处于就绪态时通知我们)直到我们显示地通过epoll_ctl()的EPOLL_CTL_DEL操作将其从列表中移除。如果我们希望在某个特定的文件描述符上只得到一次通知,那么可以在传给epoll_ctl()的ev.events中指定EPOLLONESHOT标志。如果指定了这个标志,那么在下一个epoll_wait()调用通知我们对应的文件描述符处于就绪态之后,这个描述符就会在兴趣列表中被标记为非激活态,之后的epoll_wait()调用都不会再通知我们有关这个描述符的状态了。如果需要,我们可以稍后用过调用epoll_ctl()的EPOLL_CTL_MOD操作重新激活对这个文件描述符的检查。
  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值