epoll linux内核中可扩展IO时间处理机制。处理高并发服务程序。 epoll的使用
int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核需要监听的数目一共有多大。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,第一个是epoll的句柄,第二表示动作:
EPOLL_CTL_ADD //注册新的fd到epfd中;
EPOLL_CTL_MOD //修改已经注册的fd的监听事件;
EPOLL_CTL_DEL //从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t;
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); 参数events用来从内核得到事件的集合,maxevents 告之内核这个events有多大,这个 maxevents 的值不能大于创建 epoll_create() 时的size,参数 timeout 是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。
struct epoll_event
是用于描述在使用 epoll
系统调用中的事件的结构体。它定义在 <sys/epoll.h>
头文件中,通常具有以下形式:
struct epoll_event {
uint32_t events; // 表示关注的事件类型
epoll_data_t data; // 用户数据,可以是文件描述符或指针等
};
其中:
events
字段是一个表示关注的事件类型的位掩码。它可以包含以下几个常用的标志:
EPOLLIN
:表示关注读事件(文件描述符可读)。EPOLLOUT
:表示关注写事件(文件描述符可写)。EPOLLERR
:表示关注错误事件。EPOLLHUP
:表示关注挂起事件(通常表示连接被对端关闭)。EPOLLET
:表示使用边缘触发模式。
这些标志可以通过位运算进行组合,例如,EPOLLIN | EPOLLOUT
表示同时关注读和写事件。
data
字段是一个联合体,用于存储与文件描述符相关的数据。它的定义如下:
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
具体来说:
ptr
:是一个指针,可以存储用户自定义的数据。fd
:是一个整数,用于存储文件描述符。u32
和u64
:是 32 位和 64 位的无符号整数,也可以用于存储用户自定义的数据。
这个结构体用于在调用 epoll_wait
时返回关注的事件信息。在处理事件时,你可以通过检查 events
字段来确定发生了哪些事件,然后通过 data
字段获取与事件相关的数据。
示例代码中的 epoll_wait
使用了 struct epoll_event
结构,具体代码如下:
struct epoll_event events[MAX_EVENTS];
int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (int i = 0; i < num_events; ++i) {
if (events[i].data.fd == server_fd) {
// 处理新连接
// ...
} else {
// 处理已有连接的数据
// ...
}
}
在这个示例中,通过检查 events[i].data.fd
是否等于 server_fd
来区分新连接和已有连接上的数据可读。