epoll()
在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。
epoll是一种高效的管理socket的模型,相对于select和poll来说具有更高的效率和易用性。
相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。
并且,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE 1024
表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。
#include <sys/epoll.h>
int epoll_create(int size);
// 创建一个epoll的句柄(epoll专用的文件描述符),size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event event); // 该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
select()是在监听事件时告诉内核要监听什么类型的事件,而epoll()是在这里先注册要监听的事件类型。
第一个参数epfd:是epoll_create()的返回值;
第二个参数op:表示动作(要进行的操作):EPOLL_CTL_ADD:注册新的fd到epfd中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数fd:是需要监听的fd;
第四个参数:是告诉内核需要监听什么事;
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队列里。
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); // 等待事件的产生(用于轮询I/O事件的发生),类似于select()调用。
第一个参数epfd:由epoll_create 生成的epoll专用的文件描述符;
第二个参数events:用来从内核得到事件的集合;
第三个参数maxevents:表示每次能处理的最大事件数(其值不能大于创建epoll_create()时的size);
第四个参数timeout:是超时时间(毫秒,0会立即返回,-1永不超时,或是永久阻塞,直到有事件产生才触发)。
该函数返回需要处理的事件数目,返回0表示已超时,错误返回-1。