一、什么是Epoll
Epoll的角色有点像一个管理员。Epoll被创建后,用户可以把关注的文件注册给Epoll,Epoll就开始监控这些文件的状态,文件出现了用户关心的事件时,用户程序可以获取到这些事件并处理。
万物在linux上都是文件,Socket也是文件,因此,Epoll可以管理Socket的文件句柄。具体来讲,用户可以把socket句柄注册到Epoll上去,然后关注socket的建立、读写事件。Epoll的好处是,无论多少客户端来连接,都会体现为socket句柄上发生某些特定事件,这些事件只要注册了epoll就会发现,返回给我们的程序,实现高并发的通信。
二、Epoll函数
Epoll一共有三个函数:
1. 使用epoll_create创建Epoll
int epfd = epoll_create(int size);
// size : 在这个epoll上能关注的最大文件个数
// epdf : epoll 句柄
2. 使用epoll_ctl注册/删除/修改想关注的文件和事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// epfd: epoll句柄
// op: 要对文件做什么操作,
// EPOLL_CTL_ADD:注册新的fd到epfd中;
// EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
// EPOLL_CTL_DEL:从epfd中删除fd;
// fd: 被Epoll管理的文件句柄
// event:内核需要监听什么事件
3. 使用epoll_wait等待事件发生,并把事件详情返回给用户程序。
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);
// epfd: Epoll句柄
// events: 事件的集合
// maxevents: 每次接收的最大事件数;
// timeout: 等待I/O事件发生的超时值
// 返回值: 事件个数
使用epoll_wait需要注意的是:
epoll_wait等待注册在epfd上的socket fd的事件的发生,如果发生则将发生的sokct fd和事件类型放入到events数组中。同时,将注册在epfd上的socket fd的事件类型给清空,所以如果下一个循环你还要关注这个socket fd的话,则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置关注的socket fd的事件类型。这时不用EPOLL_CTL_ADD,因为socket fd还在Epoll的管理中,只是Epoll关注fd的事件类型被清空了。
三、Epoll的事件 epoll_event
简单来讲,epollevent这个结构体里,保存了两样东西:发生了什么事和哪个文件出事了。epoll_event定义为:
struct epoll_event {
__uint32_t events; /* 事件描述,出了什么事 */
epoll_data_t data; /* 文件句柄,哪个文件出事了 */
};
到底发生哪些事能被Epoll监控到呢?Epoll定义了一些宏。根据数值和可以分为以下类型,也就是说events可以取以下数值:
EPOLLIN | 触发该事件,表示对应的文件描述符上有可读数据。(包括对端SOCKET正常关闭) |
EPOLLOUT | 触发该事件,表示对应的文件描述符上可以写数据。 |
EPOLLPRI | 表示对应的文件描述符有紧急的数据可读 |
EPOLLERR | 表示对应的文件描述符发生错误 |
EPOLLHUP | 表示对应的文件描述符被挂断 |
EPOLLET | 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的 |
EPOLLONESHOT | 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。 |
那么怎么描述是哪个文件出事了呢?epoll_data_t定义为:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
epoll_data是一个联合体。 联合体的意思就是,这个epoll_data可以是void*型、可以是int型,也可以是__uint32_t,还可以是__uint64_t。但是它只能是其中一种类型。
联合体,就是将几种数据类型联合起来的一种数据结构,但是它们共用一个空间。
怎么理解呢?川剧变脸见过没,就是来来回回的变来变去,给人看的眼花缭乱的,但是不管你怎么变,面具下的脸却始终不变。同样的道理,各种数据类型就是各种脸谱,对外展现了不同的形象,唯一不变的就是它的内存空间的二进制数据始终都是一样的,它们占用的空间也是同一个地方,只是这些二进制数据当你把它看成浮点型的时候,它是浮点数;当你把它看成无符号整数的时候,就是无符号数,或者你把这些数据看成有符号的数据,那么它就是有符号数据。
转自C语言之联合体(union) (baidu.com)
https://baijiahao.baidu.com/s?id=1623457037181175751&wfr=spider&for=pc
epoll_data 联合体用来保存触发事件的某个文件描述符相关的数据,例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对应的socket文件描述符,可以把这文件描述符赋给epoll_data的fd字段以便后面的读写操作在这个文件描述符上进行。
下一篇文章分析一段具体的代码。