epoll本质原理

epoll本质原理

second60 20180622

1. epoll简介

      epollLinux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。

2. epoll优点

1. 支持一个进程打开大数目的socket描述符

(1) 查最大支持描述符:  cat /proc/sys/fd/file-max

2. IO效率不随FD数目增加而线性下降

(1) 只对活跃的描述符进行操作(select/poll 全部描述符遍历)

3. 使用mmap加速内核与用户空间的消息传递

(1) 共享内存的方式,避免用户空间和内核空间拷贝的消耗

 

3 ETLT模式

LTlevel triggered)是缺省的工作方式,并且同时支持blockno-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。

ET edge-triggered)是高速工作方式,只支持non-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fdIO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。

4. ETLT的优缺点

1. ET只触发一次,如果数据一次未读完,需程序循环读取数据,效率好,出错的可能性大

2. LT 只要有数据就一直触发,直到数据处理完成,效率比ET差,出错的可性性小

总结:如果系统

5. 头文件代码

//头文件路径:/usr/include/sys/epoll.h
#include <sys/epoll.h>
enum EPOLL_EVENTS
{
    EPOLLIN = 0x001,
    EPOLLPRI = 0x002,
    EPOLLOUT = 0X004,
    EPOLLRDNORM = 0x040,
    EPOLLRDBAND = 0x080,
    EPOLLWRNORM = 0x100,
    EPOLLWRBAND = 0x200,
    EPOLLMSG = 0x400,
    EPOLLERR = 0x008,
    EPOLLHUP = 0x010,
    EPOLLRDHUP = 0x2000,
    EPOLLWAKEUP = 1u << 29,
    EPOLLONESHOT = 1u << 30,
    EPOLLET = 1u << 31,
};
#define EPOLL_CTL_ADD 1
#define EPOLL_CTL_DEL 2
#define EPOLL_CTL_MOD 3

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

struct epoll_event
{
    uint32_t events;
    epoll_data_t data;
} __EPOLL_PACKED;

// 创建一个epoll文件描述符
// size是内核保证能够正确处理的最大句柄数,多于这个数内核不保证效果。
int epoll_create(int __size);
int epoll_create1(int __flags);

// 添加/修改/删除需要侦听的文件描述符及其事件
// 每次添加/修改/删除被侦听的文件描述符都需调epoll_ctl,
// 所以尽量少调epoll_ctl,防止其所引来的开销
// 如应用中大量短连接(web服务器),频繁调用,可以成为系统瓶颈
int epoll_ctl(int __epfd, int __op, int __fd,struct epoll_event *_event);

// 接收发生在被侦听的描述符上的用户感兴趣的IO事件
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);

6.  EPOLL 本质原理

1. 内核初始化时,epoll向内核注册一个文件系统,同时开辟出epoll的高速cache区。

    高速cache: 就是连续的物理内存页,然后在上建立slab.

 

2. epoll_create时,在epoll文件系统中创建一个file结点,同时在高速cache区,创建红黑树和就绪链表。

    红黑树:  用来存储外部传来的文件描述符。

    就绪链表:用来存储准备就绪的事件。

 

3. epoll_ctl: 把文件描述符和事件存放到file对象对应的红黑树上,然后给内核中断处理程序注册一个回调函数

告诉内核,如果这个句柄的中断到了,就把它放到准备就绪的list链表中。

 

4. 当一个socket上有数据了,内核把网卡上的数据copy到内核中后,就把socket放到准备就结的链表中。

 

5. epoll_wait: 调用时,仅返回就绪链表中的数据,把准备就绪的socket拷贝到用户态内存,并清空就绪列表。上层程序获取有效事件并处理。

 

ET模式和LT模式,内核处理:

epoll_wait返回后会清空就绪列表

1. ET模式,不用处理,清空后不会再通知

2. LT模式,socket上有未处理事件,又把该句柄加入就绪链表。

 

7  epoll 处理最大连接数

  可查看 /proc/sys/fs/file-max文件, 根据内存而定,1G内存大约10万个,8G内存大约80万人。16G内存大约160万个文件描述符。

  但这些要根据系统内存和已使用的文件描述符而定。有人在8G内存的linux系统下测试,可以同时连接70万左右连接。理论上估计16G内存, 可以同时连接100万描述符。

 这里说的是一个epoll的连接数,证明了epoll的强大和高效。

总结

结合了网上博客总结了epoll的实现原理6中介绍了本质原理,可以画些图来表示。

 

参考资料

epoll原理

https://baike.baidu.com/item/epoll/10738144?fr=aladdin

slab原理

https://baike.baidu.com/item/slab/5803993?fr=aladdin

epoll详解

http://www.cnblogs.com/ajianbeyourself/p/5859998.html


<think>我们正在讨论epoll的工作原理,用户要求详细解释epoll的工作机制和原理。根据之前的引用和回答,我们已经知道epoll_create创建了epoll实例,现在需要深入整个epoll的工作机制。 根据引用[1]:epoll实现的数据结构包括红黑树和就绪链表,引用[2]提到epoll通过功能分离(epoll_create, epoll_ctl, epoll_wait)来改进效率,引用[3]提到从进程调度角度看数据接收,引用[4]提到epoll的三个系统调用,引用[5]提到四个重要函数(包括epoll_event_callback)。 因此,我们可以将epoll的工作原理分为以下几个部分: 1. 数据结构:红黑树和就绪链表 2. 三个系统调用的工作流程:epoll_create, epoll_ctl, epoll_wait 3. 事件回调机制 4. 边缘触发(ET)和水平触发(LT)的实现 5. 与select/poll的区别 注意:根据用户要求,我们需要在回答中自然添加引用标识,并且使用LaTeX格式的数学表达式(如果有的话),但epoll原理中数学表达式较少,可能涉及时间复杂度,如$O(1)$等。 另外,用户要求生成相关问题,放在最后。 回答结构: 一、epoll的核心数据结构 二、epoll的三个系统调用详解 三、事件触发与回调机制 四、ET与LT模式的工作原理 五、epoll高效的原因(与select/poll对比) 六、总结 最后生成相关问题 注意:由于引用标记需要自然添加,我们在每个引用观点后添加[^n]。</think>### epoll 工作原理详解 epoll 是 Linux 高性能 I/O 多路复用的核心机制,其设计通过**三级协作**实现高效事件管理:内核数据结构、协议栈通信和事件回调机制。以下是深度解析: --- #### 一、核心数据结构 1. **红黑树(`epoll instance`)** - 由 `epoll_create` 创建,存储所有监控的文件描述符(FD) - **$O(\log n)$ 复杂度**:插入/删除 FD 的时间效率(如 `epoll_ctl` 操作)[^1] - 键为 FD 值,值为监控事件(`EPOLLIN`/`EPOLLOUT` 等) 2. **就绪链表(Ready List)** - 内核动态维护的**双向链表**,存储已就绪的 FD - `epoll_wait` 直接读取该链表,实现 **$O(1)$ 事件获取** - 避免遍历所有 FD(select/poll 的 $O(n)$ 缺陷)[^2][^4] --- #### 二、工作流程与协议栈协作 ```mermaid graph LR A[应用调用 epoll_wait] --> B[阻塞等待事件] B --> C{协议栈数据到达} C -->|触发回调| D[将FD加入就绪链表] D --> E[epoll_wait 返回就绪FD] E --> F[应用处理数据] ``` 1. **协议栈通知时机** - 当网卡收到数据包 → 内核协议栈处理完成 → **触发中断** - 协议栈检查 FD 是否被 epoll 监控 → 是则调用回调函数 2. **回调函数(`epoll_event_callback`)** - 内核自动调用,核心操作: ```c void epoll_event_callback(struct file *file, int ready_mask) { struct epitem *epi = file->private_data; list_add_tail(&epi->rdllink, &epi->ep->rdllist); // 加入就绪链表 wake_up(epi->ep->wq); // 唤醒阻塞的 epoll_wait } ``` - 此过程**完全在内核态完成**,零用户-内核切换开销[^1][^3] --- #### 三、ET(边沿触发) vs LT(水平触发) | **特性** | **LT(默认)** | **ET(高效模式)** | |----------------|-----------------------------------|----------------------------------| | **触发条件** | FD 有未处理数据即持续通知 | 仅在 FD 状态变化时通知一次 | | **实现机制** | 就绪链表不清除 FD,直到数据被读完 | 回调后立即从就绪链表移除 FD | | **性能影响** | 可能多次唤醒(数据分片到达时) | 单次唤醒,需应用一次性处理所有数据 | | **应用场景** | 编程简单(如 HTTP 服务器) | 高性能场景(如 Redis)[^5] | --- #### 四、与 select/poll 的对比 | **维度** | **select/poll** | **epoll** | |------------------|----------------------------------|----------------------------------| | **数据结构** | 线性数组 | 红黑树 + 就绪链表 | | **FD 数量上限** | `FD_SETSIZE`(默认 1024) | 仅受系统内存限制 | | **时间复杂度** | $O(n)$ 遍历所有 FD | $O(1)$ 获取就绪事件 | | **内核协作** | 每次调用需全量传递 FD 集合 | 注册一次,内核维护状态 | | **内存拷贝** | 每次调用拷贝 FD 集合到内核 | 零拷贝(共享内存) | > 💡 关键优势:epoll 将 **FD 监控**(`epoll_ctl`)和 **事件等待**(`epoll_wait`)分离,避免重复传递 FD 集合[^2][^4]。 --- #### 五、多线程安全与锁机制 - **自旋锁保护就绪链表**: 当协议栈回调函数操作链表时,短暂加锁(微秒级) - **红黑树使用 RCU(Read-Copy-Update)**: 支持多线程并发操作 `epoll_ctl`,读操作无锁[^1] - **最佳实践**: - 单线程用 `epoll_wait`,多线程处理就绪事件 - 避免多线程同时调用 `epoll_ctl`(需应用层加锁) --- ### 总结 epoll 的高效本质源于: 1. **数据结构优化**:红黑树管理 FD($O(\log n)$),就绪链表加速事件获取($O(1)$) 2. **回调驱动机制**:协议栈直接触发内核回调,零用户-内核切换 3. **状态分离**:`epoll_ctl` 注册监控,`epoll_wait` 专注事件等待 4. **ET/LT 模式**:适应不同性能需求场景 该机制使 epoll 成为 C10K+ 高并发场景的核心技术(如 Nginx 每秒处理 5 万请求)[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值