Epoll
无论是从设备节点中获取原始输入事件还是从inotify对象中读取文件系统事件,都面临一个问题,就是这些事件都是偶发的。也就是
说,大部分情况下设备节、inotify对象这些文件描述符中都是无数据可读的,同事有希望有事件到来时可以尽快地对事件做出反应。为
解决这个问题,我们不希望不断地轮询这些描述符,也不希望为每一个描述符创建一个单独的线程进行阻塞时在读取,因为这都会导致资
源被极大的浪费。
此时最佳的办法是使用Epoll机制。Epoll机制可以使用一次等待监听多个描述符的可读/可写状态。等待返回时携带了可读的描述符或
者自定义的数据,使用者据此读取所需的数据后可以再次进入等待。因此不需要为每一个描述符创建对的线程进行阻塞读取,避免了资源浪
费的同时又可以获得较快的响应速度。
Epoll机制的接口:
int epoll_create(int max_fds);
创建一个epoll对象的描述符,之后对epoll的操作均使用这个描述符完成,参数max_fds表示此epoll对象可以监听的描述符的最大数量
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* events);
用于管理注册事件的函数,这个函数可以增加/删除/修改事件的注册
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
用于等待事件的到来。当此函数返回时,events数组参数中将会包含产生事件的文件描述符
int epoll_pwait(int, struct epoll_event*, int, int, const sigset_t*);
以监控若干描述符可读事件为例介绍epoll的用法
创建epoll对象
int epfd;
// Epoll FD list size hint.
static const int EPOLL_SIZE_HINT = 8;
epfd = epoll_create(EPOLL_SIZE_HINT);
填充epoll_event结构体
为每一个需监控的描述符填充epoll_event结构体,以描述监控事件,并通过epoll_ctl()函数将此描述符与epoll_event结构体注册
进epoll对象
epoll_event结构体:
struct epoll_event {
uint32_t events; /* 事件掩码,指明需要监听的事件种类 */
epoll_data_t data; /* 使用者自定义的数据,当次事件发生时,该数据将原封不懂的返回给使用者*/
} ;
epoll_data_t联合体的定义如下,当然,同一时间使用者只能使用一个字段
typedef union epoll_data {
void* ptr;
int fd;
uint32_t u32;
uint64_t u64;
}
epoll_event结构中的events字段是一个事件掩码,用以子明需要监听的事件种类,同INotify一样,掩码的每一位代表一种事件。
常用的事件有EPOLLIN(可读)、EPOLLOUT(可写)、EPOLLERR(描述符发生错误)、EPOLLHUP(描述符被挂起)等
data字段是一个联合体,它让使用者可以将一些自定义的数据加入事件通知中,当此事件发生是,用户设置的data字段将会返回给使
用者。在实际使用中经常设置epoll_event.data.fd为需要监听的文件描述符,事件发生是便可以根据epoll_event_data.fd得知
引发事件的描述符。当然,也可以设置epoll_event.data.fd为其他便于识别的数据。
填充epoll_event的方法如下:
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN | EPOLLERR | EPOLLHUP; //监听描述符可读以及出错的事件
eventItem.data.fd = listeningFd; //填写自定义数据为需要监听的描述符
使用epoll_ctl将事件注册进epoll对象
/**
epfd是由epoll_create函数所创建的epoll对象的描述符
op表示 EPOLL_CTL_ADD/DEL/MOD三种操作,分别表示增加/删除/修改注册事件
fd表示需要监听的描述符
event参数是描述监听事件的详细信息的epoll_event结构体
*/
result = epoll_ctl(epfd, EPOLL_CTL_ADD, listeningFd, &eventItem);
重复这个步骤可以将多个文件描述符的多种事件监听注册到epoll对象中。
使用epoll_wait()函数等待事件
epoll_wait()函数将会是调用者陷入等待状态,知道其注册的事件之一发生之后才返回,并且携带刚刚发生的事件的详细信息
/**
epfd是由epoll_create函数所创建的epoll对象的描述符
events参数是一个epoll_event的数组,此函数返回时,事件的信息将被填充至此
maxevents表示此次调用最多可以获取多少个事件,当然,events参数必须能足够容纳这么多事件
timeout表示等待超时的时间
*/
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
处理事件
epoll_wait()返回后,便可以根据events数组中所保存的所有epoll_events结构体的events字段与data字段识别事件的类型与来源
Epoll使用总结
通过epoll_create()创建epoll对象
为需要监听的描述符填充epoll_event结构体
使用epoll_ctl将事件注册进epoll对象
使用epoll_wait()函数等待事件
根据epoll_wait()返回的epoll_events结构体数组判断事件的类型与来源并进行处理
继续使用epoll_wait()等待新事件发生