多路复用epoll

Epoll:性能之王(红黑树+回调)

epoll 是为了解决大批量句柄处理而设计的

一、 Epoll 的核心工作原理

要理解 Epoll 的接口,首先要理解它在内核里建立的两个"秘密基地":

  1. 红黑树 (Red-Black Tree, rbr**)**:
    • 用来存储所有你想要监听的文件描述符(FD)。
    • select 每次都要把整个集合传给内核,而 Epoll 把这些 FD 存在内核的红黑树里,不需要重复拷贝 。增删查改的效率是 O(logN)。
  1. 就绪双向链表 (Ready List, rdlist**)**:
    • 用来存储已经就绪(有数据来了)的事件。
    • 当网卡接收到数据,会触发回调函数 (ep_poll_callback),这个回调函数会自动把活跃的 FD 放入这个链表 5。
    • epoll_wait 不需要遍历所有连接,只需要检查这个链表是否为空即可,复杂度是 O(1)

二、 Epoll 的三个核心接口详解

Epoll 把 select 那个臃肿的一个函数拆分成了三个步骤,分别对应"建群"、"加人"、"等消息" 。

1. epoll_create ------ 创建管家(建群)
复制代码
int epoll_create(int size);
  • 功能 :创建一个 Epoll 的句柄(在内核中创建一个 eventpoll 对象)。
  • 参数 size:
    • 这个参数其实是被忽略的,只要填一个大于 0 的数即可 。
  • 返回值 :成功返回一个 epoll 的文件描述符(epfd),失败返回 -1。
  • 注意 :Epoll 句柄本身也是一个文件描述符,占用一个 FD 资源,用完后必须调用 close() 关闭 。
2. epoll_ctl ------ 管理名单(加人/踢人)

这是 Epoll 与 Select 最大的不同点。Select 是在等待时才给名单,Epoll 是提前注册。

复制代码
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  • 参数详解:
    • epfd:epoll_create 返回的那个句柄 。
    • op:要进行的操作动作,用宏表示 :
      • EPOLL_CTL_ADD:注册新的 FD 。
      • EPOLL_CTL_MOD:修改已有的 FD 监听事件 。
      • EPOLL_CTL_DEL:从红黑树中删除一个 FD 。
    • fd:需要监听的 socket 文件描述符 。
    • event:告诉内核你要监听什么事(读/写/异常)以及你留的"备注数据" 。

关键结构体 epoll_event:

复制代码
struct epoll_event {
uint32_t     events;      // 监听的事件类型
epoll_data_t data;        // 用户数据(给用户自己用的)
};
    • events****取值:
      • EPOLLIN:可读(包括对端关闭)。
      • EPOLLOUT:可写 。
      • EPOLLET:开启边缘触发模式 (Edge Triggered)。
    • data****联合体 : 这是一个联合体,你可以存 int fd,也可以存 void *ptr 指针。当事件就绪时,内核会把这个 data 原样还给你。
3. epoll_wait ------ 等待消息(收割)
复制代码
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  • 功能 :收集在 Epoll 监控中已经发生的事件 。它直接查看内核的 rdlist 就绪链表,如果有数据,就拷贝给用户。
  • 参数详解:
    • epfd:Epoll 句柄。
    • events:输出参数。这是一个数组,内核会把就绪的事件复制到这里 。
    • maxevents:告诉内核这个数组有多大(不能大于创建时的 size,但现在 size 被忽略,主要是防止数组越界)。
    • timeout:超时时间(毫秒)。0 表示立即返回(非阻塞),-1 表示永久阻塞,>0 表示等待指定时间 。
  • 返回值:
    • > 0:就绪的文件描述符个数。
    • 0:超时。
    • -1:出错 。

三、 Epoll 的两种工作模式:LT vs ET

1. LT 模式 (Level Triggered) ------ 水平触发
  • 默认模式。
  • 机制 :只要 socket 接收缓冲区里还有数据,epoll_wait 就会一直通知你 。
  • 例子 :缓冲区有 2KB 数据,你只读了 1KB。下次调用 epoll_wait,它会再次返回并告诉你"有数据读" 。
  • 特点:支持阻塞和非阻塞读写,编程不容易出错 。Select 和 Poll 本质上也是 LT 模式 。
2. ET 模式 (Edge Triggered) ------ 边缘触发
  • 高性能模式(Nginx 默认采用)。
  • 机制:只有当状态发生变化时(从无数据变成有数据),内核才会通知一次 。
  • 例子 :缓冲区有 2KB 数据,你只读了 1KB。下次调用 epoll_wait,它不会再通知你了 ,剩下的 1KB 数据会一直待在缓冲区里,直到下一次有新数据到来 。
  • 要求:
    1. 必须配合 非阻塞 I/O (Non-blocking IO) 使用 。
    2. 必须使用循环 (while) 读取,直到 read 返回 EAGAIN 错误,确保把缓冲区读空 。
  • 优点 :减少了 epoll_wait 返回的次数,系统调用开销更小 。
相关推荐
一隅论数智3 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
未济3 天前
linux 配置环境变量
linux
傲世仙尊3 天前
目录即文件-Ext文件系统收尾篇
linux·c语言
虎头金猫3 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
_艾伦 耶格尔.3 天前
进程间通信
linux
深圳老胡3 天前
STM32F407 控制 L6470 步进电机驱动 —— 控制过程简介
笔记·stm32·单片机·嵌入式硬件·代码规范
Liuqy-053 天前
Linux IO编程——静态库、动态库
linux
wuyk5553 天前
《WiFi 嵌入式物联网开发全套实战》| 第 16 章 ESP32 AP+STA 双模共存原理与工程坑点
网络·stm32·物联网
Because_of_Her13 天前
并查集-听课笔记
笔记·算法·并查集
彧azz3 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试