epoll的系统调用很简单,只有三个,其定义如下:
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
一,epoll的实现原理就是基于这三个函数来实现的,具体步骤如下:
首先,需要调用epoll_create来创建一个epoll的文件描述符,内核会同时创建一个eventpoll的数据结构。这个数据结构里面会包含两个东西,一个是红黑树,专门用于存储epoll_ctl注册进来的fd文件描述符;另外一个是就绪链表,用来存储epoll_wait调用相关的,已经就绪的那些fd文件描述符。
图片来自网络,如有侵权,请告知,会及时删除struct eventpoll{
struct rb_root rbr; // 红黑树的根节点,存储着所有添加到epoll中的需要监控的事件
struct list_head rdlist;// 双链表中存放着将要通过epoll_wait返回给用户的满足条件的事件
};
其次,因为epoll中的所有事件,都与网卡驱动程序建立回调关系,当相应的事件发生的时候,会通过这个回调函数,将发生的事件添加到就绪链表当中。
最后,当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有需要处理的事件。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。
二,系统函数介绍:
1、epoll_create
调用epoll_create方法创建一个epoll的句柄,该句柄会占用一个fd,用完之后需要回收。
2、epoll_ctl
epoll的事件注册函数,通过epoll_ctl注册要监听的事件类型。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd:epoll_create创建的epollfd。
events:表示动作类型。三中类型,如下所示:
1) EPOLL_CTL_ADD:注册新的fd到epfd中;
2) EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
3) EPOLL_CTL_DEL:从epfd中删除一个fd。
fd:需要监听的fd。
event:告诉内核需要监听的事件。(EPOLLIN:表示对应的文件描述符可读(包括对端Socket); EPOLLOUT:表示对应的文件描述符可写; EPOLLPRI:表示对应的文件描述符有紧急数据可读(带外数据); EPOLLERR:表示对应的文件描述符发生错误; EPOLLHUP:表示对应的文件描述符被挂断; EPOLLET:将EPOLL设为边缘触发(Edge Triggered),这是相对于水平触发(Level Triggered)而言的。 EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket,需要再次调用epoll_ctl)
3、epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
功能:收集在epoll监控的事件中已经发生的事件。
参数:
events:是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据赋值到这个event数组中,不会去帮助我们在用户态分配内存)。
maxevents:告诉内核这个events数组有多大,这个maxevents的值不能大于创建epoll_create时的size。
timeout:是超时时间(毫秒)。
返回值:如果函数调用成功,则返回对应IO上已准备好的文件描述符数目,如果返回0则表示已经超时。
备注: 参考博客:
https://blog.csdn.net/zhaobryant/article/details/80557262
https://blog.csdn.net/shenya1314/article/details/73691088
灰子做于二零一九年二月十六日。
网友评论