51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Linux Epoll IO 模型

Epoll IO 模型是 Linux 中用于 I/O 多路复用的机制,可以用于监听多个文件描述符上的事件,以及非阻塞地等待这些事件的发生。其工作机制大致如下:

  1. 首先,初始化一个 Epoll 实例,这个实例主要在内核中维护的两个数据结构,一个红黑树,用于存储被检测的文件描述符,一个链表,用于存储就绪事件
  2. 然后,我们将要监控的文件描述符放到 Epoll 实例的红黑树中,然后内核开始遍历红黑树检测文件描述符是否有注册的事件发生
  3. 当有事件发生时,内核会将这个事件添加到链表中,等待用户处理
  4. 用户执行操作后,就绪事件就会从内核的链表中拷贝到用户空间中,用户就可以取出事件进行处理

在这个过程中,我们发现内核使用了红黑树来维护和管理待监控的文件描述符集合,红黑树并没有文件描述符上限的要求,所以,只要内存足够,我们可以添加更多的文件描述符到 Epoll 的内核红黑树中。这也有利于提高 epoll 的并发量。

但是,需要注意的是,在 Linux 系统中,默认情况下,每个进程可以打开的文件描述符数量是有限制的。如果超出了文件描述符的限制,就会出现无法打开新的文件描述符的情况,影响程序的正常运行。进程可以配置最大的文件描述符数量。

另外,与 Select IO 模型相比,每次的 Select 调用都需要进行两次内核和用户空间的数据交换。而 Epoll 则是在通知用户事件时,拷贝一次。这非常有利于提高 epoll 的性能。

用户处理事件时,不需要遍历所有的文件描述符,因为 epoll 只会返回已就绪的文件描述符,这也有利于提高 epoll 的性能。

下面是 Epoll 操作相关函数:

// 创建 epoll 实例
int epoll_create1 (int __flags)
  • __flags:如果设置为 EPOLL_CLOEXEC 表示程序在退出之前关闭文件描述符

函数如果失败,则返回 -1,可通过 errno 来查看具体错误信息

int epoll_ctl (int __epfd, int __op, int __fd, struct epoll_event *__event)
  • __epfd:epoll 实例的文件描述符。
  • __op:要执行的操作,可以是以下值之一:
    • EPOLL_CTL_ADD:向 epoll 实例注册一个新的文件描述符。
    • EPOLL_CTL_MOD:修改已注册文件描述符的事件。
    • EPOLL_CTL_DEL:从 epoll 实例中删除一个文件描述符。
  • __fd:要注册、修改或删除的文件描述符。
  • __event:指向 epoll_event 结构体的指针,用于描述要注册、修改或删除的事件。可以为 NULL(仅在执行 EPOLL_CTL_DEL 操作时)

函数成功返回 0,否则返回 -1,可通过 errno 来指示错误。

int epoll_wait (int __epfd, struct epoll_event *__events, int __maxevents, int __timeout)
  • __epfd:epoll 实例的文件描述符。
  • __events:指向 epoll_event 结构体数组的指针,用于存储就绪的文件描述符和事件。
  • __maxeventsevents 数组中能存储的最大事件数。
  • __timeout:等待就绪事件的超时时间(以毫秒为单位)。可以为以下值之一:
    • -1:无限等待。
    • 0:立即返回。
    • 大于 0 的整数:等待指定毫秒数后返回。

函数执行成功,则返回就绪的文件描述符数量,否则返回 -1,可通过 errno 指示错误。

示例代码:

#include <cstdio>
#include <sys/epoll.h>
#include <set>

#define MAX_EVENT_NUM 128

void test() { // 初始化 Epoll 对象 int epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (-1 == epoll_fd) { perror("epoll_create1 error"); return; }

// 已连接的文件描述符
std::set&lt;int&gt; fds = { 100, 101, 102, 103, 104 };

int ret = -1;

// 注册文件描述符
for (auto fd : fds)
{
    // 设置文件描述符绑定的读写事件
    struct epoll_event fd_event;
    fd_event.events = EPOLLIN;  // 写事件
    fd_event.data.fd = fd;
    // 将文件描述符和事件存储到红黑树中
    ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &amp;fd_event);
    if (-1 == ret)
    {
        perror(&quot;epoll_ctl error&quot;);
        continue;
    }
}

// 从 Epoll 中删除某个文件描述符
int del_fd = 101;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, del_fd, nullptr);
if (-1 == ret)
{
    perror(&quot;epoll_ctl error&quot;);
    return;
}
// 从 fds 中也要删除文件描述符
fds.erase(del_fd);

// 开始监控文件描述符
struct epoll_event events[MAX_EVENT_NUM];
ret = epoll_wait(epoll_fd, events, MAX_EVENT_NUM, -1);
if (-1 == ret)
{
    perror(&quot;epoll_wait error&quot;);
    return;
}

}

int main() { test(); return 0; }

赞(5)
未经允许不得转载:工具盒子 » Linux Epoll IO 模型