epoll 是 Linux 內核提供的異步事件通知機制。應用程式不再輪詢,而是向內核註冊關注的文件描述符 (fd) 及事件類型;當 fd 就緒(可讀/可寫)時,內核通過 epoll_wait 主動通知應用層,應用層再發起實際 recv/send。epoll 不直接進行 I/O 讀寫,僅負責事件通知,保持通知與操作解耦,極大提升靈活性。
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; // 關注/觸發的事件位掩碼
epoll_data_t data; // 用戶自定義數據 (如 fd、指針)
};
EPOLLIN:可讀 (含對端正常關閉)EPOLLOUT:可寫EPOLLPRI:帶外數據/緊急數據可讀EPOLLERR:錯誤EPOLLHUP:掛起/斷開EPOLLET:邊緣觸發 (ET) 模式 (默認水平觸發 LT)EPOLLONESHOT:一次性通知,處理完需重新註冊int epoll_create(int size);
// size: 提示內核監聽規模 (舊內核用於哈希表大小,新內核忽略)
// 返回: epoll 專用 fd (需 close 釋放)
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// op: EPOLL_CTL_ADD (註冊) | EPOLL_CTL_MOD (修改) | EPOLL_CTL_DEL (刪除)
// event: 指定關注事件及用戶數據
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
// events: 輸出數組,存放就緒事件
// maxevents: 數組容量 (≤ epoll_create 的 size)
// timeout: 毫秒;-1 永久阻塞,0 立即返回
// 返回: 就緒事件數;0 超時;-1 出錯
| 特性 | 水平觸發 (LT, 默認) | 邊緣觸發 (ET) |
|---|---|---|
| 通知條件 | fd 就緒狀態持續存在即反覆通知 | fd 狀態發生變化瞬間通知一次 |
| 編程模型 | 可阻塞/非阻塞均可 | 必須非阻塞 + 循環讀寫至 EAGAIN |
| 遺漏風險 | 無 | 若未讀寫完畢,下次不再通知,導致數據滯留 |
| 適用場景 | 簡單、穩健 | 高併發、追求極致效能 |
FD_SETSIZE (通常 1024) 限制;epoll 僅受系統最大 fd 數限制。epoll_data 直接攜帶用戶數據指針,避免輪詢傳遞大量 fd 集合。while (1) {
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < n; ++i) {
int fd = events[i].data.fd;
if (events[i].events & EPOLLIN) {
while (1) {
ssize_t r = read(fd, buf, sizeof(buf));
if (r > 0) process(buf, r);
else if (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) break; // 讀完
else { close(fd); break; } // 錯誤或對端關閉
}
}
// 處理 EPOLLOUT 同理
}
}
epoll 是 Linux 高併發網絡編程基石。掌握 LT/ET 差異、非阻塞配合、EPOLLONESHOT 線程安全用法,能構建百萬級併發服務器。