epoll机制
本文承接 nativePollOnce详解,不写脱离 Android 的 epoll API 教程,而是追踪 libutils::Looper 如何维护 FD 请求、把序列号写入 epoll_event,并在回调修改或关闭 FD 后拒绝旧 response。
1. 两张表
源码文件:system/core/libutils/include/utils/Looper.h
相关字段:mRequests、mSequenceNumberByFd、mRequestsGeneration
std::unordered_map<SequenceNumber, Request> mRequests;
std::unordered_map<int, SequenceNumber> mSequenceNumberByFd;
std::atomic<uint64_t> mRequestsGeneration;
struct Request {
int fd;
int ident;
int events;
sp<LooperCallback> callback;
void* data;
};一张表由序列号找到完整 Request,另一张表由 FD 找当前序列号。epoll 的 data.u64 保存序列号而不是裸 FD,避免关闭后快速复用数字造成旧事件误认。
2. 注册流程
源码文件:system/core/libutils/Looper.cpp
相关函数:addFd()
if (!callback.get()) {
if (!mAllowNonCallbacks || ident < 0) return -1;
} else {
ident = POLL_CALLBACK;
}
std::scoped_lock _l(mLock);
const SequenceNumber seq = mNextRequestSeq++;
Request request{fd, ident, events, callback, data};
mRequestsGeneration.fetch_add(1, std::memory_order_release);
epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);callback 非空时 ident 被强制改为 POLL_CALLBACK;无 callback 只有在允许该模式且 ident 非负时合法。每次添加或替换都增加 generation。
源码文件:system/core/libutils/Looper.cpp
相关函数:addFd()
auto seq_it = mSequenceNumberByFd.find(fd);
if (seq_it == mSequenceNumberByFd.end()) {
epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
mRequests.emplace(seq, request);
mSequenceNumberByFd.emplace(fd, seq);
} else {
epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
// 删除旧序列号,写入新 Request
}同一个 FD 再注册是替换,不是追加第二个监听器。旧 response 不会自动修改,而是靠序列号和 generation 在消费时失效。
3. 等待返回
源码文件:system/core/libutils/Looper.cpp
相关函数:pollInner()、pollOnce()
int eventCount = epoll_wait(epollFd, eventItems,
EPOLL_MAX_EVENTS, timeoutMillis);
for (int i = 0; i < eventCount; i++) {
const SequenceNumber seq = eventItems[i].data.u64;
const auto& request_it = mRequests.find(seq);
if (request_it != mRequests.end()) {
const auto& request = request_it->second;
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
mResponses.push_back({.seq = seq, .events = events, .request = request});
}
}查不到序列号的事件被忽略。pollOnce() 下一轮先消费 mResponses,因此一次 epoll_wait 可以产生多个待处理 response。
4. 过期检查
源码文件:system/core/libutils/Looper.cpp
相关函数:isResponseStale()
bool Looper::isResponseStale(const uint64_t generation,
const SequenceNumber& seq) const {
if (mRequestsGeneration.load(std::memory_order_acquire) == generation) {
return false;
}
std::scoped_lock _l(mLock);
return mRequests.find(seq) == mRequests.end();
}generation 未变化时走无锁快路径;变化后再确认序列号是否仍在 Request 表。删除或替换 FD 后,旧 response 因此不会调用新 listener。
5. 锁外回调
源码文件:system/core/libutils/Looper.cpp
相关函数:pollInner()
const uint64_t generation = mRequestsGeneration.load(std::memory_order_acquire);
mLock.unlock();
for (Response& response : mResponses) {
if (response.request.ident != POLL_CALLBACK) continue;
if (isResponseStale(generation, response.seq)) continue;
int callbackResult = response.request.callback->handleEvent(
response.request.fd, response.events, response.request.data);
if (callbackResult == 0) {
std::scoped_lock _l(mLock);
removeSequenceNumberLocked(response.seq);
}
response.request.callback.clear();
}回调锁外执行,因为它可能关闭、重新注册或删除其他 FD。返回 0 才注销当前请求,callback 引用随后清空。
6. 删除与复用
源码文件:system/core/libutils/Looper.cpp
相关函数:removeFd()、removeSequenceNumberLocked()、scheduleEpollRebuildLocked()
int Looper::removeFd(int fd) {
std::scoped_lock _l(mLock);
auto it = mSequenceNumberByFd.find(fd);
if (it == mSequenceNumberByFd.end()) return 0;
return removeSequenceNumberLocked(it->second);
}
// removeSequenceNumberLocked 更新两张表、generation,并执行 EPOLL_CTL_DEL。当旧 FD 已关闭而同一个数字被新 FD 复用,EPOLL_CTL_MOD 可能返回 ENOENT。源码会尝试 EPOLL_CTL_ADD,并安排 rebuildEpollLocked():重建时关闭旧 epoll,重新注册 wake FD 和当前 Request 表。
7. 单测输入
源码文件:system/core/libutils/Looper_test.cpp
相关测试:PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent()、ThreeCallbacksSignalInOnePoll()、PollOnce_RemovePendingFdInCallback()
pipe.writeSignal();
mLooper->addFd(pipe.receiveFd, expectedIdent,
Looper::EVENT_INPUT, nullptr, expectedData);
int result = mLooper->pollOnce(100, &fd, &events, &data);
EXPECT_EQ(expectedIdent, result);
EXPECT_EQ(pipe.receiveFd, fd);
EXPECT_EQ(Looper::EVENT_INPUT, events);
EXPECT_EQ(expectedData, data);这些测试分别验证非 callback response 的 ident/fd/data、同一轮三个 FD 的回调数量,以及一个回调删除其他待处理 FD 后旧 response 被跳过。
8. 动手验证
rg -n "mRequests|mSequenceNumberByFd|mRequestsGeneration|epoll_ctl|epoll_wait" \
system/core/libutils/Looper.cpp system/core/libutils/include/utils/Looper.h
rg -n "PollOnce_WhenNonCallbackFdIsSignalled|ThreeCallbacksSignalInOnePoll|RemovePendingFdInCallback" \
system/core/libutils/Looper_test.cpp9. 边界说明
本文不展开 Linux 内核 epoll 数据结构、pollAll() 或 Java 同步屏障。固定源码支持的是 Looper 对 epoll 的请求表、序列号、response 失效和 FD 复用恢复。
