epoll API介绍

        epoll 是一个 Linux 系统调用,用于监控多个文件描述符,查看是否可以在其中任何一个文件描述符上进行 I/O 操作。它常用于网络编程,用于构建可扩展的高效服务器。

        epoll API 的核心概念是 epoll 实例,它是一种内核数据结构,从用户空间的角度来看,可以将其视为两个列表的容器:

        ①. The interest list(兴趣列表): 进程已注册要监控的文件描述符集;                

        ②. The ready list(就绪列表):"就绪 "可进行 I/O 的文件描述符集合。ready list是“兴趣列表”中文件描述符的子集(或者更准确地说,是一组引用)。内核会根据这些文件描述符上的 I/O 活动动态填充“就绪列表”。

        以下系统调用可用于创建和管理 epoll 实例:

1. epoll_create

#include <sys/epoll.h>

int epoll_create(int size);
int epoll_create1(int flags);

        epoll_create() 创建一个新的 epoll 实例。自 Linux 2.6.8 起,size 参数将被忽略,但必须大于 0;

        epoll_create() 返回一个指向新 epoll 实例的文件描述符。该文件描述符将用于随后对 epoll 接口的所有调用不再需要时,应使用 close() 关闭 epoll_create() 返回的文件描述符。当引用 epoll 实例的所有文件描述符都被关闭后,内核将销毁该实例,并释放相关资源以供重用。

        epoll_create1() 如果 flags 为 0,则除了放弃过时的 size 参数外,epoll_create1() 与 epoll_create() 相同。可以在 flags 中加入以下值,以获得不同的行为:EPOLL_CLOEXEC

EPOLL_CLOEXEC:设置新文件描述符的执行时关闭 (FD_CLOEXEC) 标志。

2. epoll_ctl

        通过 epoll_ctl()注册特定文件描述符,将其添加到 epoll 实例的interest list中。

#include <sys/epoll.h> #include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *_Nullable event);

       用于添加、修改或删除文件描述符 epfd 所映射的epoll实例的“兴趣列表”。它要求对目标文件描述符 fd 执行操作 op。

        op参数的有效值为:

①. EPOLL_CTL_ADD:

        EPOLL_CTL_ADD 在 epoll 文件描述符 epfd 的“兴趣列表”中添加一个条目。该条目包括文件描述符 fd、相应的打开文件描述的引用,以及 event 中指定的设置。

②EPOLL_CTL_MOD:

        EPOLL_CTL_MOD 将“兴趣列表”中与 fd 相关的设置更改为事件中指定的新设置。

③EPOLL_CTL_DEL:

        EPOLL_CTL_DEL 从“兴趣列表”中删除目标文件描述符 fd。事件参数将被忽略,可以为 NULL;

        event参数描述了与文件描述符 fd 相链接的对象

#include <sys/epoll.h>

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

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

typedef union epoll_data  epoll_data_t;

        epoll_event 结构的数据成员指定内核应保存的数据,然后在该文件描述符就绪时(通过 epoll_wait())返回。

        epoll_event 结构的events成员是一个位掩码,由 epoll_wait() 返回的零个或多个事件类型和输入标志组成,输入标志会影响其行为,但不会返回。常用的事件类型有: EPOLLIN、EPOLLOUT;

        函数返回值:成功时,epoll_ctl() 返回 0;发生错误时,epoll_ctl() 返回-1,并设置 errno 表示错误。

3. epoll_wait

#include <sys/epoll.h>

int epoll_wait(int epfd, struct epoll_event *events, 
    int maxevents, int timeout);
int epoll_pwait(int epfd, struct epoll_event *events, 
    int maxevents, int timeout, const sigset_t *_Nullable sigmask);
int epoll_pwait2(int epfd, struct epoll_event *events, 
    int maxevents, const struct timespec *_Nullable timeout, 
    const sigset_t *_Nullable sigmask);

        等待 I/O 事件,如果当前没有可用事件,则阻塞调用线程。(可视为从 epoll 实例的“就绪列表”中获取node)。

        epoll_wait() 系统调用会等待文件描述符 epfd 所指向的 epoll实例上的事件。事件指向的缓冲区用于从“就绪列表”中返回关于“兴趣列表”中具有某些可用事件的文件描述符的信息。epoll_wait() 最多会返回 maxevents,maxevents 参数必须大于零

        参数timeout指定了 epoll_wait() 将阻塞的毫秒数。时间是根据 CLOCK_MONOTONIC 时钟计算的

        调用 epoll_wait() 会阻塞,直到出现以下任一情况:

                ①. 文件描述符传递事件;

                ②. 调用被信号处理器中断;

                ③. 超时结束;

        需要注意的是,超时间隔将按系统时钟粒度四舍五入,而内核调度延迟意味着阻塞间隔可能会超时一小段时间指定的超时时间为 -1 会导致 epoll_wait() 无限期阻塞,而指定的超时时间等于零则会导致 epoll_wait() 立即返回,即使没有可用的事件

        返回值:成功时,epoll_wait() 返回已为请求的 I/O 做好准备的文件描述符的数量,如果在请求的超时毫秒内没有文件描述符做好准备,则返回 0。如果失败,epoll_wait() 返回 -1 并设置 errno 表示错误。 

4. C++示例程序 

#include <iostream>
#include <sys/epoll.h>
#include <unistd.h>

int main() {
    // Create an epoll instance
    int epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        std::cerr << "Failed to create epoll instance\n";
        return 1;
    }

    // Create a pipe for demonstration purposes
    int pipe_fds[2];
    if (pipe(pipe_fds) == -1) {
        std::cerr << "Failed to create pipe\n";
        return 1;
    }

    // Add the read end of the pipe to the epoll instance
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = pipe_fds[0];  // Add the read end of the pipe
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipe_fds[0], &event) == -1) {
        std::cerr << "Failed to add file descriptor to epoll\n";
        return 1;
    }

    // Demonstrate waiting for events
    while (true) {
        constexpr int max_events = 10;
        struct epoll_event events[max_events];

        // Wait for events
        int num_events = epoll_wait(epoll_fd, events, max_events, -1);
        if (num_events == -1) {
            std::cerr << "epoll_wait error\n";
            return 1;
        }

        // Handle events
        for (int i = 0; i < num_events; ++i) {
            if (events[i].events & EPOLLIN) {
                // Event is ready for reading
                std::cout << "Data is available to read from the pipe\n";

                // Read data from the pipe
                char buffer[100];
                ssize_t bytes_read = read(pipe_fds[0], buffer, sizeof(buffer));
                if (bytes_read == -1) {
                    std::cerr << "Read error\n";
                    return 1;
                }

                // Print the read data
                std::cout << "Read " << bytes_read << " bytes: " << buffer << "\n";
            }
        }
    }

    // Close resources
    close(pipe_fds[0]);
    close(pipe_fds[1]);
    close(epoll_fd);

    return 0;
}

        该演示创建了一个 epoll 实例,将管道的读取端添加到该实例中,然后进入一个事件循环,使用 epoll_wait 等待事件发生。.当可以从管道中读取数据时,它会读取并打印数据。这是一个简化的示例;在实际应用中,您需要处理更复杂的情况,并更谨慎地管理资源。

  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值