学习使用epoll
epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
一、epoll的优点
支持一个进程打开大数目的socket描述符。
IO效率不随FD数目增加而线性下降。
二、epoll的使用
epoll有2种工作方式:LT和ET。
LT(level triggered,水平触发)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,
内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任操作,
内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型
的代表。
ET (edge-triggered,边缘触发)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未
就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文
件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在
发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是
请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知
(only once)。
epoll相关的系统调用有3个:epoll_create, epoll_ctl和epoll_wait。在头文件<sys/epoll.h>
http://dl.iteye.com/upload/attachment/545833/3e114d46-115d-3f09-91de-108cc6dca156.jpg
1. int epoll_create(int size);
创建一个epoll句柄,即图中的epfd, 用来监听事件, size用来告诉内核这个监听的数目一共有多大。
这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数op是操作类型, 使用这个方法完成3种操作:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
(1) 注册新事件
struct epoll_event ev;ev.data.fd = fd;ev.events = EPOLLIN;epoll_cntl(epfd, EPOOL_CTL_ADD, fd, &ev);
(2) 修改监听事件
struct epoll_event *a_event = get_a_event() struct epoll_event ev;ev.data.fd = a_event->data.fd;ev.events = a_event->events | EPOLLOUT;epoll_cntl(epfd, EPOOL_CTL_MOD, a_event->data.fd, &ev);
(3) 删除事件
struct epoll_event *a_event = get_a_event() struct epoll_event ev;ev.data.fd = a_event->data.fd;epoll_cntl(epfd, EPOOL_CTL_DEL, a_event->data.fd, &ev);
3种操作都使用了一个ev变量, 这个变量用来关联fd和它的监听事件, 是临时的, 可以反复使用.
ev是一个struct epoll_event结构体, 结构如下:
<div class="dp-highlighter nogutter">
[*]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 */
[*]};
页:
[1]