epoll函数及调用
1、调用epoll_create函数创建epoll对象:int epoll_create(int size)
返回一个句柄,之后epoll的使用都将依靠这个句柄来标识。
参数size告诉epoll所要处理的大致事件数目。2、调用epoll_ctl向epoll对象中添加、修改、删除所要监控连接的套接字:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
epfd是epoll描述符。
op参数为EPOLL_CTL_ADD(添加事件), EPOLL_CTL_MOD(修改事件), EPOLL_CTL_DEL(删除事件)三种。
fd参数是待检测的连接套接字。
event参数告诉epoll对怎样的事件感兴趣。其中 epoll_event 结构体定义
struct epoll_event{
__uint32_t events;
epoll_data_t data;
}events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里3、掉用epoll_wait从epoll对象中收集发生事件的连接:int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
返回当前发送的事件个数。0:没有发送事件,-1:为出现错误。
epfd是epoll描述符。
events是由进程自身分配好的epoll_event结构体数组,内核负责将数据复制到该数组中。
maxevents是可以返回事件的最大个数,一般events数组的大小。
timeout为没有检测到事件发送最大等待时间。
epoll的Linux内核实现浅谈
Linux内核的一些数据结构跟Nginx特别像尤其像链表、树结构。可以通过我的这篇博客:Nginx数据结构学习_向往天空的鱼-CSDN博客大概了解一下上述数据结构的实现原理。
1、当某一进程调用epoll_create创建epoll对象时,Linux内核会为该epoll对象创建一个独立的eventpoll结构体,该结构体有两个与epoll调用密切相关的变量,如下:
struct eventpoll{
......
struct rb_root rbr; /* 红黑树的根节点,存储着该epoll对象所有监控的事件 */
struct list_head rdllist; /* 双向列表的头结点,存储epoll_wait返回给用户的事件 */
......
}2、在epoll对象中,对应每一个监控的事件都会创建一个epitem结构体,包含每一个事件实时对应的信息,如下:
struct epitem{
......
struct rb_node rbn; /* 红黑树的节点 */
struct list_head rdllink; /* 双向列表的结点 */
struct epoll_filefd ffd; /* 事件句柄等信息 */
struct eventpoll *ep; /* 指向其所属的eventpoll对象 */
struct epoll_event event; /* 期待的事件类型 */
......
}上述两个结构体在epoll内部是如何安排布置的,如下图:
当进程调用epoll_wait检查是否有发生事件的连接时,只需要检查对象中的rdllist双链表是否为空链表,如果不为空,则把这里的事件复制到用户态的内存中,同时将事件数量返回给进程。因此,epoll_wait的效率非常高。epoll_ctl在向epoll对象中添加、修改、删除事件时,从rbr红黑树中查找事件也非常快,也就是说,epoll是非常高效的,它可以轻易的处理百万级别的并发连接。
epoll模型的代码实现及惊群效应的深入理解详解笔者另一篇博文:epoll模型实现及惊群效应_码农诗人的博客-CSDN博客,欢迎指正。