Android随笔-epoll机制

epoll 是 Linux 内核的 I/O 多路复用 机制,也是 Android Looper 底层实现的核心。它解决了传统 select/poll 的性能瓶颈,实现了 O(1) 的事件通知效率。

一、为什么需要 epoll?(select/poll 的问题)

机制 问题
select 1024 FD 上限,每次调用都要拷贝整个 FD 集合 到内核,内核遍历所有 FD 检查状态,再拷贝回用户态
poll 用链表突破了 1024 限制,但仍然是 O(n) 遍历
epoll O(1) 事件驱动,注册一次,内核主动通知

核心痛点:

  • select/poll 每次调用都要扫描所有 FD(即使大部分没有事件)
  • 连接数上万时(C10K 问题),CPU 大量消耗在遍历上

二、epoll 的核心数据结构(内核)

c 复制代码
// Linux Kernel: fs/eventpoll.c

// (1) eventpoll - epoll 实例
struct eventpoll {
    spinlock_t lock;                    // 自旋锁
    struct mutex mtx;                   // 互斥锁
    wait_queue_head_t wq;               // epoll_wait() 的等待队列
    struct list_head rdllist;           // (2) 就绪链表 - 存储已就绪的 FD
    struct rb_root_cached rbr;          // (3) 红黑树 - 存储所有被监控的 FD
    struct epitem *ovflist;             // 溢出链表
};

// (4) epitem - 每个被监控的 FD 对应一个
struct epitem {
    struct rb_node rbn;                 // 红黑树节点
    struct list_head rdllink;         // 就绪链表节点
    struct eventpoll *ep;               // 所属的 eventpoll
    struct epoll_event event;           // 事件掩码(EPOLLIN 等)
    struct file *file;                  // 被监控的文件
};

双数据结构设计:

  • 红黑树 (rbr):存储所有被监控的 FD,O(log n) 插入/删除/查找
  • 就绪链表 (rdllist):存储已就绪的 FD,epoll_wait() 直接返回,O(1)

三、epoll 的三个核心 API

c 复制代码
// 1. 创建 epoll 实例
int epfd = epoll_create1(EPOLL_CLOEXEC);

// 2. 注册/修改/删除 FD
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);   // 添加
epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &event);   // 修改
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);     // 删除

// 3. 等待事件(阻塞/非阻塞)
int nfds = epoll_wait(epfd, events, maxevents, timeout);

四、核心机制:事件回调(The Magic)

epoll 不是轮询,而是事件驱动

c 复制代码
// fs/eventpoll.c - ep_poll_callback

// 当被监控的 FD 状态变化时,内核自动调用这个回调
static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, 
                            int sync, void *key) {
    struct epitem *epi = ep_item_from_wait(wait);
    struct eventpoll *ep = epi->ep;
    
    spin_lock(&ep->lock);
    
    // (1) 将 FD 加入就绪链表
    if (!ep_is_linked(&epi->rdllink)) {
        list_add_tail(&epi->rdllink, &ep->rdllist);
    }
    
    // (2) 唤醒在 epoll_wait() 中睡眠的进程
    if (waitqueue_active(&ep->wq)) {
        wake_up_locked(&ep->wq);
    }
    
    spin_unlock(&ep->lock);
    return 1;
}

关键洞察:

  • 当网络数据到达、pipe 有数据、eventfd 被写入时,内核的网络栈/文件系统会自动调用 ep_poll_callback
  • 不需要用户态轮询,内核主动通知

五、epoll_wait() 的工作流程

c 复制代码
// fs/eventpoll.c - ep_poll

static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
                    int maxevents, long timeout) {
    
    // (1) 快速路径:检查就绪链表
    if (!list_empty(&ep->rdllist)) {
        goto send_events;  // 已有就绪事件,直接返回!
    }
    
    // (2) 没有就绪事件,需要睡眠
    for (;;) {
        set_current_state(TASK_INTERRUPTIBLE);  // 标记为可中断睡眠
        
        // (3) 双检锁:再检查一次(避免竞态)
        if (!list_empty(&ep->rdllist)) {
            set_current_state(TASK_RUNNING);
            break;
        }
        
        // (4) 真正的阻塞:让出 CPU
        schedule_timeout(timeout);  // 线程在这里睡眠!
    }
    
send_events:
    // (5) 将就绪事件拷贝到用户空间
    return ep_send_events(ep, events, maxevents);
}

六、水平触发(LT)vs 边缘触发(ET)

模式 行为 特点
LT(Level Triggered) 只要 FD 就绪,epoll_wait 持续返回 默认模式,安全,可能重复通知
ET(Edge Triggered) 只在状态变化时通知一次 高效,必须一次性处理完所有数据

类比:

  • LT 像快递员小王:你不下楼,他一直在楼下喊
  • ET 像快递员小赵:喊一次你不下来,他就走了

七、与 Android Looper 的关联

Android 的 Looper 本质上是对 epoll 的封装:

  • Looper 创建一个 epoll 实例
  • 将 eventfd(或旧版的 pipe)注册到 epoll
  • nativePollOnce() 调用 epoll_wait() 睡眠
  • nativeWake() 写入 eventfd,触发 epoll_wait() 返回

八、总结

epoll 是 Linux 内核的高性能 I/O 多路复用机制。它通过「红黑树」管理所有被监控的文件描述符,通过「就绪链表」记录已就绪的事件,通过「事件回调」机制让内核主动通知(而非轮询)。epoll_wait() 在没有事件时让线程睡眠,有事件时立即唤醒,实现了 O(1) 的事件处理效率,与连接数无关。

相关推荐
plainGeekDev1 小时前
Gradle 写法 → Kotlin DSL
android·java·kotlin
三少爷的鞋1 小时前
协程深度解析:明明只有 8 个异步任务,为何 App 线程数瞬间突破 一倍?
android
GitLqr11 小时前
Flutter 3.44 插件内置 Kotlin (KGP) 双向兼容适配指南
android·flutter·dart
随遇丿而安16 小时前
第11周:Activity 跳转与传值 + 跳转优化
android
私人珍藏库17 小时前
[Android] BBLL 开源第三方B哩电视TV端
android·app·生活·工具·多功能
杉氧19 小时前
跨平台资源管理:一套代码如何搞定 Android、iOS 和 Web 的图片与多语言?
android·架构·android jetpack
安卓修改大师20 小时前
安卓修改大师实战:从反编译到定制的完整APK修改指南
android
柚鸥ASO优化21 小时前
安卓APP推广的“降本增效”密码:守好商店内,打好商店外
android·aso优化
我是一颗柠檬1 天前
【Java项目技术亮点】EXPLAIN深度分析与慢查询治理
android·java·开发语言