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:是一个整数,用于存储文件描述符。
  • u32u64:是 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 来区分新连接和已有连接上的数据可读。