示范代码-辅助理解

写一个最小可运行的 C 程序,模拟 Android 消息机制中底层用到的:

  • epoll_wait():阻塞等待事件
  • eventfd():作为唤醒机制
  • write():模拟 nativeWake()
  • read():模拟被唤醒后读取

这个程序可以帮助你直观理解:Android 如何通过 epoll + eventfd 实现线程阻塞等待 + 唤醒机制

代码示例:模拟 Android 的 Looper 机制(最小实现)

// looper_simulation.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>

int g_event_fd;

void* wake_thread_func(void* arg) {
    sleep(2);  // 等待主线程先阻塞在 epoll_wait

    uint64_t val = 1;
    write(g_event_fd, &val, sizeof(val));  // 模拟 nativeWake()

    printf("[wake_thread] Sent wake-up signal via eventfd.\n");
    return NULL;
}

int main() {
    // 1. 创建 eventfd
    g_event_fd = eventfd(0, 0);
    if (g_event_fd == -1) {
        perror("eventfd");
        exit(EXIT_FAILURE);
    }

    // 2. 创建 epoll
    int epfd = epoll_create1(0);
    if (epfd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    // 3. 注册 eventfd 到 epoll
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = g_event_fd;

    if (epoll_ctl(epfd, EPOLL_CTL_ADD, g_event_fd, &ev) == -1) {
        perror("epoll_ctl");
        exit(EXIT_FAILURE);
    }

    // 4. 启动唤醒线程
    pthread_t tid;
    pthread_create(&tid, NULL, wake_thread_func, NULL);

    printf("[main_thread] Waiting for events (epoll_wait)...\n");

    // 5. 阻塞等待事件
    struct epoll_event events[1];
    int nfds = epoll_wait(epfd, events, 1, -1);  // -1 表示永久阻塞
    if (nfds == -1) {
        perror("epoll_wait");
        exit(EXIT_FAILURE);
    }

    if (events[0].data.fd == g_event_fd) {
        uint64_t count;
        read(g_event_fd, &count, sizeof(count));  // 清空 eventfd,避免重复触发
        printf("[main_thread] Received wake-up signal! count = %lu\n", count);
    }

    close(g_event_fd);
    close(epfd);

    return 0;
}

编译运行

gcc looper_simulation.c -o looper_simulation -lpthread
./looper_simulation

输出说明

你会看到:

[main_thread] Waiting for events (epoll_wait)...
[wake_thread] Sent wake-up signal via eventfd.
[main_thread] Received wake-up signal! count = 1

这完全模拟了 Android 中:

  • Looper.loop() 中的 nativePollOnce() 被阻塞;
  • 有消息进来 → 调用 nativeWake();
  • epoll 被 eventfd 唤醒;
  • 读取消息后继续处理。

如何类比 Android:

示例代码行为 Android 中的类比
epoll_wait() nativePollOnce() 阻塞等待事件
eventfd 注册到 epoll 注册消息/唤醒 fd 到 epoll
write(eventfd) nativeWake() 唤醒主线程
read(eventfd) MessageQueue.next() 获取事件

逐行、逐层深入讲解这份示范代码,帮助你建立对 epoll + eventfd 机制的理解。

程序结构总览

int main() {
    // 初始化 eventfd(唤醒机制)
    // 初始化 epoll(事件等待器)
    // 将 eventfd 注册进 epoll
    // 启动另一个线程 2 秒后写入 eventfd
    // 主线程阻塞在 epoll_wait
    // 一旦被唤醒,读取 eventfd 并打印结果
}

详解每一部分


eventfd创建

g_event_fd = eventfd(0, 0);

解释:

  • eventfd 是 Linux 提供的 计数器式唤醒机制,返回一个 文件描述符
  • 后续可以向它 write() 一个整数来递增内部计数;
  • read() 可以读取这个计数,并清除它
  • 被 epoll 监视时,当其内部值非零,它就变成「可读」,epoll_wait 就会返回。

对应 Android:

  • Android 中的 nativeWake() 本质就是向这个 eventfd 写入 1,唤醒 epoll。

创建 epoll 实例

int epfd = epoll_create1(0);

解释:

  • 创建一个 epoll 实例;
  • 返回的是另一个文件描述符(epfd);
  • 后续所有的等待、注册事件,都是通过它完成。

类比 Android:

  • Android C++ 层的 Looper 在 init() 时就会创建 epoll 实例,挂在 mEpollFd 上。

注册 eventfd 到 epoll

ev.events = EPOLLIN;               // 关注可读事件
ev.data.fd = g_event_fd;           // 附加数据,告诉 epoll 是哪个 fd

epoll_ctl(epfd, EPOLL_CTL_ADD, g_event_fd, &ev);

解释:

  • 注册 “关注某个 fd 的可读事件” 到 epoll;
  • 后续 epoll_wait() 检测到这个 fd 可读,就会返回。

对应 Android:

  • Android Looper 会把 eventfd、socket、Binder fd 全部注册到 epoll 中,等它们变成可读或写。

启动“唤醒线程”

pthread_create(&tid, NULL, wake_thread_func, NULL);
  • 开一个新线程,模拟 Android 中的 Handler.sendMessage();
  • 这个线程睡 2 秒,然后向 eventfd 写入一个整数,触发唤醒。

wake_thread_func 的逻辑

sleep(2);  // 主线程此时应该已经进入 epoll_wait
uint64_t val = 1;
write(g_event_fd, &val, sizeof(val));

解释:

  • 延迟 2 秒,模拟“任务发生”;
  • 向 eventfd 写入 1 → epoll_wait() 中的主线程将被唤醒;
  • 注意:必须写入 uint64_t 类型,否则行为未定义。

对应 Android:

  • Handler.sendMessage() 最终会调用 nativeWake(),往 eventfd 写入。

主线程阻塞等待 epoll 事件

epoll_wait(epfd, events, 1, -1);

解释:

  • epoll 等待你注册的所有 fd 上的事件;
  • -1 表示无限等待;
  • 一旦 eventfd 可读(即被写入),就返回。

唤醒后读取 eventfd

if (events[0].data.fd == g_event_fd) {
    uint64_t count;
    read(g_event_fd, &count, sizeof(count));
    printf("[main_thread] Received wake-up signal! count = %lu\n", count);
}

解释:

  • 必须 读取 eventfd,否则它会一直保持“可读”状态,epoll_wait() 会立刻返回;
  • 这也是为什么 Android 的 nativePollOnce() 之后需要清理状态。

执行流程图

主线程:                          唤醒线程:
--------                         ----------------
创建 epoll
创建 eventfd
注册 eventfd → epoll
启动 wake_thread →(睡眠2秒)

epoll_wait() ← 阻塞在此       (睡2秒)
                                ↓
                          write(eventfd) → 写入1
                                ↓
唤醒!返回 epoll_wait
读取 eventfd → 打印 count

对应 Android 消息机制:

示例中行为 Android 对应
epoll_wait() nativePollOnce()
eventfd 写入 nativeWake()
read(eventfd) 处理消息后清空唤醒状态
pthread_create() 多线程发消息
fd 注册到 epoll Looper::addFd()、Binder fd、eventfd

总结(要点记住)

机制 用途
epoll 高效等待多个事件源
eventfd 通知机制,适合线程间唤醒
epoll_wait + eventfd 就是 Android Looper 等待和唤醒的核心
必须 read(eventfd) 否则 epoll 永远可读,造成 busy-loop
Android 的 nativePollOnce 就是这样实现的 100% 对应

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器