9.6.2 如何使用epoll

epoll通过下面3个epoll系统调用为用户提供服务。

(1)epoll_create系统调用

epoll_create在C库中的原型如下。


int epoll_create(int size);


epoll_create返回一个句柄,之后epoll的使用都将依靠这个句柄来标识。参数size是告诉epoll所要处理的大致事件数目。不再使用epoll时,必须调用close关闭这个句柄。

注意 size参数只是告诉内核这个epoll对象会处理的事件大致数目,而不是能够处理的事件的最大个数。在Linux最新的一些内核版本的实现中,这个size参数没有任何意义。

(2)epoll_ctl系统调用

epoll_ctl在C库中的原型如下。


int epoll_ctl(int epfd,int op,int fd,struct epoll_event*event);


epoll_ctl向epoll对象中添加、修改或者删除感兴趣的事件,返回0表示成功,否则返回-1,此时需要根据errno错误码判断错误类型。epoll_wait方法返回的事件必然是通过epoll_ctl添加到epoll中的。参数epfd是epoll_create返回的句柄,而op参数的意义见表9-2。

9.6.2 如何使用epoll - 图1

第3个参数fd是待监测的连接套接字,第4个参数是在告诉epoll对什么样的事件感兴趣,它使用了epoll_event结构体,在上文介绍过的epoll实现机制中会为每一个事件创建epitem结构体,而在epitem中有一个epoll_event类型的event成员。下面看一下epoll_event的定义。


struct epoll_event{

__uint32_t events;

epoll_data_t data;

};


events的取值见表9-3。

9.6.2 如何使用epoll - 图2

而data成员是一个epoll_data联合,其定义如下。


typedef union epoll_data{

void

*ptr;

int

fd;

uint32_t

u32;

uint64_t

u64;

}epoll_data_t;


可见,这个data成员还与具体的使用方式相关。例如,ngx_epoll_module模块只使用了联合中的ptr成员,作为指向ngx_connection_t连接的指针。

(3)epoll_wait系统调用

epoll_wait在C库中的原型如下。


int epoll_wait(int epfd,struct epoll_event*events,int maxevents,int timeout);


收集在epoll监控的事件中已经发生的事件,如果epoll中没有任何一个事件发生,则最多等待timeout毫秒后返回。epoll_wait的返回值表示当前发生的事件个数,如果返回0,则表示本次调用中没有事件发生,如果返回-1,则表示出现错误,需要检查errno错误码判断错误类型。第1个参数epfd是epoll的描述符。第2个参数events则是分配好的epoll_event结构体数组,epoll将会把发生的事件复制到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存。内核这种做法效率很高)。第3个参数maxevents表示本次可以返回的最大事件数目,通常maxevents参数与预分配的events数组的大小是相等的。第4个参数timeout表示在没有检测到事件发生时最多等待的时间(单位为毫秒),如果timeout为0,则表示epoll_wait在rdllist链表中为空,立刻返回,不会等待。

epoll有两种工作模式:LT(水平触发)模式和ET(边缘触发)模式。默认情况下,epoll采用LT模式工作,这时可以处理阻塞和非阻塞套接字,而表9-3中的EPOLLET表示可以将一个事件改为ET模式。ET模式的效率要比LT模式高,它只支持非阻塞套接字。ET模式与LT模式的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的;而LT模式则相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。因此,在LT模式下开发基于epoll的应用要简单一些,不太容易出错,而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。默认情况下,Nginx是通过ET模式使用epoll的,在下文中就可以看到相关内容。