Linux多路转接epoll

ops/2025/2/5 19:55:10/

Linux多路转接 epoll

epoll() 解决了 poll() 的部分缺陷,epoll 消除了线性扫描,使用了红黑树结构来存储监听的事件,同时也能避免注册重复文件描述符。

epoll 被公认为 Linux2.6 下最好的多路转接 IO 就绪通知方法。

1. epoll 的工作原理

epoll 将使用的函数增加到了三个,epoll_create()epoll_ctl()epoll_wait()

首先用户要先使用 epoll_create() 注册一个 epoll 句柄,这个函数会在内核建立一个 eventpoll 结构体,这个结构体就代表了 epoll 实例,在这个 eventpoll 中,有两个关键的结构,一个红黑树,用于存储注册的文件描述符和事件信息;一个就绪链表(rdlist),存储发生了事件的文件描述符

红黑树中的每个节点都是一个 epitem 结构体,这个结构体存储了一个文件描述符及其相关的事件信息

struct epitem {struct list_head  rdllink;     // 用于将触发事件的 `epitem` 放入就绪链表struct rb_node    rbn;         // 红黑树节点,链接到 `epitem` 的红黑树中struct epoll_filefd {struct file   *file;       // 指向文件对象的指针,用于关联文件描述符int           fd;          // 文件描述符} ffd;struct eventpoll *ep;          // 指向所属的 `epoll` 实例struct list_head  fllink;      // 链接到 `epoll` 实例的 `allitems` 链表中struct epoll_event event;      // 事件信息,包括监听的事件类型和触发的回调
};

当用户使用 epoll_ctl() 向 epoll 实例注册文件描述符时,本质会在这个红黑树中插入一个节点(key 为文件描述符),或使用这个函数来修改已经注册的文件描述符,也是通过红黑树的查找和修改来实现的,所以 epoll 在管理大量文件描述符上有优势。

所有添加到 epoll 中的事件都会与设备(网卡)驱动程序建立回调关系,即当响应的事件发生时会调用这个回调方法,这个回调方法在内核中叫 ep_poll_callback(),它会将发生的事件添加到 rdlist 双链表中。所以用户调用 epoll_wait() 时,其只需要在 rdlist 就绪链表中查看是否有 epitem 元素即可。如果 rdlist 不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。 这个操作的时间复杂度是 O(1)。

Epoll1

Epoll2

硬件中断通知操作系统有数据要读,调用ep_poll_callback()而不是操作系统主动轮询去读才知道有事件发生。

2. 函数声明

#include <sys/epoll.h>

int epoll_create(int size)

用于创建一个 epoll 的句柄。

size 是一个已经被废弃的参数,只要设置大于 0 即可。

reval 返回值是一个文件描述符,小于 0 表示失败。

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

epfd 是 epoll 的句柄,即 epoll_create() 的返回值。

op 一般有三个操作:

  • EPOLL_CTL_ADD:向 epfd 中注册新的 fd。
  • EPOLL_CTL__MOD:修改已经注册的 fd 的监听事件。
  • EPOLL_CTL_DEL:从 epfd 中删除一个 fd。

fd 需要监听的文件描述符。

event 是事件集,下面给出 struct epoll_event 的具体结构。

reval 返回值为 0 表示成功,返回值为 -1 表示失败。

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

epoll_wait()会将就绪的事件,依次严格按照顺序放入我们定义的用户缓冲区数组

epfd 是一个文件描述符,是 epoll_create() 的返回值。

*events 是一个数组,实质上是传入一个事件缓冲区,从内核捞取就绪事件给用户。

maxevents 是数组的最大容量,表示一次最多可以返回的事件数。

timeout 表示超时时间,以毫秒为单位;设置 0 表示非阻塞等待;设置 -1 表示阻塞等待。

reval 成功则返回就绪文件描述符的数量,返回值等于 0 表示超时,返回值小于 0 表示发生错误。

3. struct epoll_event

typedef union epoll_data
{void        *ptr;	/* 通用指针,用户可以保存自定义数据指针 */int          fd;	/* 文件描述符 */uint32_t     u32;	/* 32 位整数 */uint64_t     u64;	/* 64 位整数 */
} epoll_data_t;struct epoll_event
{uint32_t     events;      /* 事件掩码,表示感兴趣的或发生的事件 */epoll_data_t data;        /* 用户数据,可以是文件描述符或指针 */
};

events:表示希望监视的事件类型或实际发生的事件类型。它的值是多个 epoll 事件类型的按位或运算结果。

epoll_data_t: 是一个类型的联合体,允许用户在事件发生时存储自定义数据。可以是一个指针、文件描述符、32 位或 64 位整数,开发者可以根据需要选择其中一种方式存储数据。

常见事件:

  • EPOLLIN:表示对应的文件描述符上有数据可读(包括对端 SOCKET 正常关闭)。
  • EPOLLOUT:表示对应的文件描述符可以写数据。
  • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(表示有带外数据到来)。
  • EPOLLHUP:表示对应的文件描述符被挂断。
  • EPOLLET:将 EPOLL 设置为边沿触发模式(Edge Triggered),这是相对于水平触发(Level Triggered)来说的。
  • EPOLLONESHOT:只监听一次事件,当监听完此事件后,如果还需要继续监听该 socket ,则必须将该 socket 重新加入到 EPOLL 实例中。
  • EPOLLRDHUP:对端关闭连接。
  • EPOLLERR:文件描述符发生错误。

4. 水平触发模式和边缘触发模式

4.1 水平触发模式

水平触发(Level Triggered)模式又称 LT 模式,是 epoll 的默认工作模式(也是 select、poll 的工作模式)。LT 模式下,当 epoll 检测到 socket 上事件就绪的时候,可以不立刻进行处理或者只处理一部分,剩下的数据等下次调用 epoll_wait() 再处理。

如果第一次数据没处理完,下次调用 epoll_wait() 会立即返回并通知 socket 事件就绪,直到缓冲区上所有的数据都被处理完,下次调用 epoll_wait() 才不会立即返回。

水平触发模式支持文件描述符为阻塞状态和非阻塞状态。

4.2 边缘触发模式

边缘触发模式(Edge Triggered)模式又称 ET 模式,是 epoll 需要手动设置为 EPOLLET 从水平触发模式切换过来的工作模式。ET 模式下,epoll 只通知事件就绪一次,本轮数据没读完,epoll不再通知。所以ET模式一旦就绪,就必须把数据全部读完。所以ET通知的效率更高。

一个事件就绪只通知一次就能全部读取,但 LT 模式下可能需要通知很多次才能全部读取完成。

ET模式要一次性把数据读取完,就要循环读取,直到读取不到。但是循环读取一定会有阻塞问题,所以ET模式下要求所有的文件描述符都使用非阻塞模式。

4.3 LT 和 ET 的优缺点

  1. LT 模式的编码要比 ET 模式更简单。

    网络通信的主机接收数据和发送 ACK 都要有严格的顺序,LT 模式很容易实现这一点,但在 ET 模式下一次性读取所有就绪的事件,就需要手动编码接收数据和发送 ACK 的顺序的控制。

  2. ET在IO上会更高效一点。

    因为 ET 模式要上层循环读直到把数据都读走,而 LT 模式可以只读走一部分数据,下次再接着读。ET 模式逼着上层尽快把 TCP 缓冲区的数据尽快取走。所以如果一个服务器处于 ET 模式,接收方的上层尽快把数据取走, TCP 给对方的 ACK 一定有较大的概率给对方通告一个更大的接收窗口,就可以让对方的发送端一次性发送更多的数据包。

  3. ET 要求程序员严格编码。

    LT 模式通过设计,也可以设计成非阻塞、循环读取,实现 ET 模式同样的功能。但 LT 模式不强制要求文件描述符为非阻塞,而ET 模式能倒闭着程序员设置非阻塞文件描述符。

4.4 epoll使用

todo

4.5 epoll 的优点

  1. 接口使用方便。

    epoll 虽然需要三个函数才能使用,但是使用起来反而更简单高效。

  2. 数据拷贝轻量。

    只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中, 这个操作并不频繁(而 select/poll 都是每次循环都要进行拷贝)。

  3. 事件回调机制效率高。

    避免使用遍历,而是使用回调函数的方式,将就绪的文件描述符结构加入到就绪队列中,epoll_wait() 返回直接访问就绪队列就知道哪些文件描述符就绪。这个操作时间复杂度 O(1)。即使文件描述符数目很多,效率也不会受到影响。

  4. 没有数量限制。

    文件描述符数目无上限。


http://www.ppmy.cn/ops/155959.html

相关文章

PyTorch快速入门

Anaconda Anaconda 是一款面向科学计算的开源 Python 发行版本&#xff0c;它集成了众多科学计算所需的库、工具和环境管理系统&#xff0c;旨在简化包管理和部署&#xff0c;提升开发与研究效率。 核心组件&#xff1a; Conda&#xff1a;这是 Anaconda 自带的包和环境管理…

[MRCTF2020]Ez_bypass1(md5绕过)

[MRCTF2020]Ez_bypass1(md5绕过) ​​ 这道题就是要绕过md5强类型比较&#xff0c;但是本身又不相等&#xff1a; md5无法处理数组&#xff0c;如果传入的是数组进行md5加密&#xff0c;会直接放回NULL&#xff0c;两个NuLL相比较会等于true&#xff1b; 所以?id[]1&gg…

图书管理系统 Axios 源码__获取图书列表

目录 核心功能 源码介绍 1. 获取图书列表 技术要点 适用人群 本项目是一个基于 HTML Bootstrap JavaScript Axios 开发的图书管理系统&#xff0c;可用于 添加、编辑、删除和管理图书信息&#xff0c;适合前端开发者学习 前端交互设计、Axios 数据请求 以及 Bootstrap 样…

解读“大语言模型(LLM)安全性测评基准”

1. 引入 OWASP&#xff0c;全称为Open Web Application Security Project&#xff0c;即开放式Web应用程序安全项目&#xff0c;是一个致力于提高软件安全性的非营利国际组织。 由于庞大的规模和复杂的结构&#xff0c;大语言模型也存在多种安全风险&#xff0c;如prompt误导…

1.[安洵杯 2019]easy_web1

打开题目页面如下 发现在url处有传参&#xff0c;且img处的参数看着像base64编码 ?imgTXpVek5UTTFNbVUzTURabE5qYz0 解码试试看&#xff0c;可以使用下面这个在线转换工具 base64解码 base64编码 在线base64解码/编码工具 iP138在线工具 解完后的编码看着还像base64编码&am…

飞桨PaddleNLP套件中使用DeepSeek r1大模型

安装飞桨PaddleNLP 首先安装最新的PaddleNLP3.0版本&#xff1a; pip install paddlenlp3.0.0b3 依赖库比较多&#xff0c;可能需要较长时间安装。 安装好后&#xff0c;看看版本&#xff1a; import paddlenlp paddlenlp.__version__ 输出&#xff1a; 3.0.0b3.post2025…

芝士AI(paperzz):最新AI论文、AI降重、AI降重工具,解决论文写作低效和AI率

相信大家都有经历过毕业论文查重&#xff0c;有些严格的学校甚至只有1次查重机会&#xff0c;令人心惊胆战。“东拼西凑”的论文怎么保证查重率符合要求成为每个大学生的毕业必修课题。 芝士AI&#xff08;paperzz&#xff09;官网&#xff1a;https://www.paperzz.cn/ 不过…

P3078[USACO13MAR] Poker Hands S

P3078[USACO13MAR] Poker Hands S https://www.luogu.com.cn/problem/P3078 前言 学习差分后写的第一道题&#xff0c;直接给我干懵逼&#xff0c;题解都看不懂……吃了个晚饭后开窍写出来了&#xff0c;遂成此篇。 题目 翻译版本 Bessie 和她的朋友们正在玩一种独特的扑克游…