示范代码-辅助理解
写一个最小可运行的 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% 对应 |