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) 的事件处理效率,与连接数无关。

相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui