epoll简介

233 阅读4分钟

epoll 是 Linux 内核提供的一种高效的 I/O 多路复用机制,相比于传统的 select 和 poll,具有更高的性能和灵活性。epoll 的核心接口主要包括以下几个:

  1. epoll_create/epoll_create1
  2. epoll_ctl
  3. epoll_wait

下面是对这些接口的详细解释:

1. epoll_create / epoll_create1

epoll_create

int epoll_create(int size);

epoll_create 创建一个新的 epoll 实例,并返回一个文件描述符用于后续操作。参数 size 并不影响 epoll 实例的性能,只是为了向后兼容旧的内核版本。

epoll_create1

int epoll_create1(int flags);

epoll_create1epoll_create 的改进版,允许通过 flags 参数设置额外的选项,比如 EPOLL_CLOEXEC,在执行 exec 系列函数时自动关闭该文件描述符。

示例:

int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
    perror("epoll_create1");
    exit(EXIT_FAILURE);
}

2. epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll_ctl 函数用于控制 epoll 实例中的文件描述符。它的参数说明如下:

  • epfd:由 epoll_createepoll_create1 返回的 epoll 实例的文件描述符。
  • op:要执行的操作,主要有以下几种:
    • EPOLL_CTL_ADD:将文件描述符 fd 添加到 epoll 实例中。
    • EPOLL_CTL_MOD:修改已经在 epoll 实例中的文件描述符 fd 的监听事件。
    • EPOLL_CTL_DEL:从 epoll 实例中删除文件描述符 fd
  • fd:要操作的目标文件描述符。
  • event:指向 epoll_event 结构体的指针,用于指定事件类型和关联的数据。

示例:

struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = listen_sock;

if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event) == -1) {
    perror("epoll_ctl: listen_sock");
    exit(EXIT_FAILURE);
}

3. epoll_wait

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epoll_wait 用于等待事件的发生。参数说明如下:

  • epfd:epoll 实例的文件描述符。
  • events:指向 epoll_event 结构体数组的指针,用于返回发生的事件。
  • maxeventsevents 数组的大小,即一次最多处理的事件数。
  • timeout:等待事件发生的超时时间,以毫秒为单位。如果设置为 -1,则表示无限等待。

示例:

struct epoll_event events[MAX_EVENTS];
int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nfds == -1) {
    perror("epoll_wait");
    exit(EXIT_FAILURE);
}

for (int n = 0; n < nfds; ++n) {
    if (events[n].events & EPOLLIN) {
        handle_incoming_data(events[n].data.fd);
    }
}

epoll_event 结构体

在上面的接口中,我们多次提到 epoll_event 结构体,它定义了 epoll 事件的类型和关联的数据。

struct epoll_event {
    uint32_t events;    /* Epoll events */
    epoll_data_t data;  /* User data variable */
};

其中,events 是一个位掩码,可以是以下值的组合:

  • EPOLLIN:表示对应的文件描述符可以读。
  • EPOLLOUT:表示对应的文件描述符可以写。
  • EPOLLRDHUP:表示对端关闭连接或半关闭连接。
  • EPOLLPRI:表示对应的文件描述符有紧急数据可读。
  • EPOLLERR:表示对应的文件描述符发生错误。
  • EPOLLHUP:表示对应的文件描述符被挂起。
  • EPOLLET:表示将文件描述符设置为边缘触发模式。
  • EPOLLONESHOT:表示一次事件后自动将文件描述符从 epoll 实例中移除。

data 是一个联合体,可以存储用户自定义的数据,如文件描述符、指针等。

typedef union epoll_data {
    void    *ptr;
    int      fd;
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

epoll 的优点

  1. 高效性:与 select 和 poll 不同,epoll 在监听大量文件描述符时不会随文件描述符数量增加而线性增长,因此更适合大规模并发。
  2. 灵活性:epoll 支持边缘触发和水平触发两种模式,可以根据需求选择合适的模式。
  3. 用户态与内核态交互减少:epoll 通过事件通知机制,减少了用户态与内核态的交互次数,从而提升了性能。

小结

epoll 提供了一种高效的 I/O 多路复用机制,特别适用于处理大规模并发连接。通过 epoll_create 创建 epoll 实例,使用 epoll_ctl 添加、修改或删除文件描述符,然后通过 epoll_wait 等待事件发生,可以构建高性能的网络服务器或其他 I/O 密集型应用。了解和使用 epoll 的这些接口,能够显著提升系统的 I/O 处理能力和整体性能。