I/O 多路复用之 Event Poll
来源:互联网 发布:三菱plc编程教学视频 编辑:程序博客网 时间:2024/05/22 20:57
概述
由于 poll() 和 select() 的局限,Linux 2.6内核引入了 event poll(epoll) 机制。虽然 epoll 的实现比 poll() 和 select() 要复杂得多,epoll 解决了前两个存在的基本性能问题,并增加了一些新的特性。
对于poll() 和 select(),每次调用时都需要所有被监听的文件描述符列表。内核必须遍历所有被监视的文件描述符。当这个文件描述符列表变得很大时——包含几百个甚至几千个文件描述符时——每次调用都要遍历列表就变成了规模上的瓶颈。
epoll 把监听注册从实际监听中分离出来,从而解决了这个问题。一个系统调用会初始化 epoll 上下文,另一个从上下文中加入或删除监视的文件描述符,第三个执行真正的事件等待(event wait)。
Event Poll
创建新的 epoll 实例
通过 epoll_create1() 创建 epoll 上下文:
#include <sys/epoll.h>
int epoll_create1 ( int flags);
/* deprecated. use epoll_create1() in new code */
int epoll_create (int size);
调用成功时,epoll_create1() 会创建新的 epoll 实例,并返回和该实例关联的文件描述符。这个文件描述符和真正的文件没有关系,仅仅是为了后续调用 epoll 而创建的。参数 flags 支持修改 epoll 行为,当前,只有 EPOLL_CLOEXEC 是个合法的 flag,它表示进程被替换时关闭文件描述符。
出错时,返回 -1,并设置 errno 为下列值之一:
EINVAL 参数 flags 非法
EMFILE 用户打开的文件数达到上限
ENFILE 系统打开的文件数达到上限
ENOMEN 内存不足,无法完成本次操作
epoll_create() 是老版本的epoll_create1()的实现,现在已经废弃。它不接受任何标志位。相反地,它接收 size 参数,该参数没有用。size 之前是用于表示要监视的文件描述符个数;现在,内核可以动态获取数据结构的大小,只需要 size 参数大于 0 即可。如果 size 值小于0,会返回 EINVAL。如果应用所运行的系统其Linux版本低于Linux内核2.6.27 以及 glibc 2.9,应该使用老的 epoll_create() 调用。
epoll 的标准调用方式如下:
int epfd;
epfd = epoll_create1 (0);
if (epfd < 0) {
perror ("epoll_create1");
}
当完成监视后,epoll_create1() 返回的文件描述符需要通过 close() 调用来关闭。
控制 epoll
epoll_ctl() 函数可以指向的 epoll 上下文中加入或删除文件描述符:
#include <sys/epoll.h>
int epoll_ctl (int epfd,
int op,
int fd,
struct epoll_event *event);
头文件<sys/epoll.h>中定义了 epoll event 结构体:
struct epoll_event {
__u32 events; /* events */
union {
void *ptr;
int fd;
__u32 u32;
__u64 u64;
} data;
};
epoll_ctl() 调用如果执行成功,会控制和文件描述符 epfd 关联的 epoll 实例。参数 op 指定对 fd 指向的文件所执行的操作。参数 event 进一步描述 epoll 更具体的行为。
以下是参数 op 的有效值:
EPOLL_CTL_ADD
把文件描述符 fd 所指向的文件添加到 epfd 指定的 epoll 监听实例集中,监听 event 中定义的事件。
EPOLL_CTL_DEL
把文件描述符 fd 所指向的文件从 epfd 指定的 epoll 监听集中删除。
EPOLL_CTL_MOD
使用 event 指定的更新事件修改在已有 fd 上的监听行为。
epoll_events 结构体中的 events 变量列出了在指定文件描述符上要监听的事件。多个监听事件可以通过位或运算同时指定。以下为有效的 events 值:
EPOLLERR
文件出错。即使没有设置,这个事件也是被监听的。
EPOLLET
在监听文件上开启边缘触发(edge-triggered) ,默认是条件触发(level-trigered) 。
EPOLLHUP
文件被挂起。即使没有设置,这个事件也是被监听的。
EPOLLIN
文件未阻塞,可读
EPOLLONESHOT
在事件生成并处理后,文件不会再被监听。必须通过EPOLL_CTL_MOD 指定新的事件掩码,以便重新监听文件。
EPOLLOUT
文件未阻塞,可写。
EPOLLPRI
存在高优先级的带外(out-of-band)数据可读。
event_poll 中的 data 变量是由用户私有使用。当接收到请求的事件后,data 会被返回给用户。通常的用法是把 event.data.fd 设置为 fd,这样可以很容易查看哪个文件描述符触发了事件。
当成功时,epoll_ctl() 返回 0。失败时,返回 -1,并相应设置 errno 为下列值:
EBADF epfd 不是有效的 epoll 实例,或者 fd 不是有效的文件描述符。
EEXIST op 值设置为 EPOLL_CTL_ADD, 但是 fd 已经与 epfd 关联。
EINVAL epfd 不是 epoll 实例, epfd 和 fd 相同, 或 op 无效。
ENOENT op 值设置为 EPOLL_CTL_MOD 或 EPOLL_CTL_DEL,但是 fd 没有和 epfd 关联。
ENOMEN 没有足够的内存来处理请求。
EPERM fd 不支持 epoll。
在下面的例子中,在 epoll 实例 epfd 中加入 fd 所指向文件的监听事件,代码如下:
struct epoll_event event;
int ret;
event.data.fd = fd; /* return the fd to us later (from epoll_wait) */
event.events = EPOLLIN | EPOLLOUT;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
if (ret) {
perror("epoll_ctl");
}
相反,从 epoll 实例epfd中删除在fd上的一个监听事件,代码如下:
struct epoll_event event;
int ret;
ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &event);
if (ret) {
perror("epoll_ctl");
}
需要注意的是,当 op 设置为 EPOLL_CTL_DEL 时,由于没有提供事件掩码,event参数可能会是NULL。但是,在2.6.9以前的内核版本中,会检查该参数是否非空。为了和老的内核版本保持兼容,必须传递一个有效的非空指针,该指针不能只是声明。内核2.6.9版本修复了这个bug。
等待 epoll 事件
系统调用 epoll_wait() 会等待和指定 epoll 实例关联的文件描述符上的事件:
#include <sys/epoll.h>
int epoll_wait(int epfd,
struct epoll_event *events,
int maxevents,
int timeout);
当调用 epoll_wait() 时,等待 epoll 实例 epfd 中的文件 fd 上的事件,时限为 timeout 毫秒。成功时,events 指向描述每个事件的epoll_event结构体的内存,且最多可以有maxevents个事件,返回值是事件数;出错时,返回 -1,并将errno设置为以下值:
EBANF epfd 是一个无效的文件描述符。
EFAULT 进程对events所指向的内存没有写权限。
EINTR 系统调用在完成前发生信号中断或超时。
EINVAL epfd 不是有效的epoll实例,或者maxevents值小于或等于0。
如果timeout为0,即使没有事件发生,调用也会立即返回0。如果timeout为 -1,调用将一直等待到有事件发生才返回。
当调用返回时,epoll_event结构体中的events变量描述了发生的事件。data 变量保留了用户在调用epoll_ctl()前的所有内容。
epoll_wait()例子如下:
#define MAX_EVENTS 64
struct epoll_event *events;
int nr_events, i, epfd;
events = malloc(sizeof(struct epoll_event) * MAX_EVENTS);
if (!events) {
perror("malloc");
return 1;
}
nr_events = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (nr_events < 0) {
perror("epoll wait");
free(events);
return 1;
}
for (i = 0; i < nr_events; ++i) {
printf("event = %d on fd = %d\n", events[i].events, events[i].data.fd);
/*
* We now can, per events[i].events, operate on
* events[i].data.fd without blocking.
*/
}
free (events);
0 0
- I/O 多路复用之 Event Poll
- I/O多路复用之poll
- I/O 多路复用之poll
- I/O多路复用之poll
- I/O多路复用之poll
- I/O多路复用之poll
- I/O多路复用之poll
- I/O多路复用之poll
- poll() | 多路复用 I/O
- poll() | 多路复用 I/O
- I/O多路复用poll
- I/O多路复用技术之 - poll
- I/O多路复用之select/poll/epoll
- I/O多路复用之poll模型
- I/O多路复用之POLL系统调用
- I/O多路复用之poll函数分析
- Linux--高级I/O多路复用之Poll
- I/O多路复用之poll服务器
- 当webview遇到360wifi
- 谈谈对Spring IOC的理解
- 3414POJ
- android中的Bitmap
- fedora20配置静态ip
- I/O 多路复用之 Event Poll
- 数位DP专题小结--by sgx
- HDU周赛题
- 黑马程序员--Java面向对象理解
- hdu 1875畅通工程续
- 关于内存中栈和堆的区别[转]
- EL表达式
- Java基础-多线程-生产者与消费者(笔记)
- 解决Android系统中CSS设置select高度无效的BUG