使用 epoll 和 kqueue 进行异步 IO
在 Linux 上使用 epoll、在 BSD 或 macOS 上使用 kqueue,扩展到处理大量连接
使用 epoll 和 kqueue 进行异步 IO 是 CoddyKit 上的免费 C++ Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C++ Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C++ Academy 课程共包含 4 节课。
为什么使用异步 I/O?
为每个连接创建一个线程无法扩展到数千甚至数百万个客户端。异步 I/O 让一个线程能够处理多个套接字,等待其中任意一个变为可读或可写。
Linux:epoll
epoll 是 Linux 的可扩展事件 API。将文件描述符添加到 epoll 实例中,然后等待其中任意描述符上的事件。
#include <sys/epoll.h>
int epfd = epoll_create1(0);
epoll_event ev{};
ev.events = EPOLLIN;
ev.data.fd = sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev);等待事件
epoll_wait 会阻塞,直到有事件可用,然后返回已就绪的描述符。
epoll_event events[64];
int n = epoll_wait(epfd, events, 64, -1);
for (int i = 0; i < n; ++i) {
int fd = events[i].data.fd;
handle_io(fd);
}边沿触发与水平触发
有两种模式:
- 水平触发(默认)——条件持续满足时,epoll 会持续报告事件
- 边沿触发(
EPOLLET)——仅在状态改变时报告;每次都必须完全读取套接字中的数据
macOS 和 BSD:kqueue
kqueue 是 BSD 对应的 epoll,也可用于 macOS。两者概念相似,但 API 不同。
#include <sys/event.h>
int kq = kqueue();
struct kevent ev;
EV_SET(&ev, sock, EVFILT_READ, EV_ADD, 0, 0, nullptr);
kevent(kq, &ev, 1, nullptr, 0, nullptr);Windows:IOCP
Windows 使用 I/O 完成端口(IOCP)——这是一种基于完成的模型(完成时通知),而不是 epoll/kqueue 所采用的就绪模型。
非阻塞套接字
请使用 fcntl(fd, F_SETFL, O_NONBLOCK) 将套接字设置为非阻塞模式。否则,读取和写入可能会阻塞整个事件循环。
事件循环模式
一个无限运行的循环:
- 等待已就绪的事件
- 针对每个事件执行 I/O,直到操作即将阻塞
- 重复上述步骤
处理大量客户端
单个 epoll 线程可以处理数万个并发连接,这就是 C10K 问题。现代系统可以进一步扩展到数百万个连接。
常见陷阱
请注意以下问题:
- 忘记设置非阻塞模式
- 使用边沿触发却没有读取完套接字中的数据
- 没有从 epoll 集合中移除已关闭的文件描述符
- 每个连接的缓冲区发生泄漏
更高层的封装
手动编写 epoll 循环容易出错。对于生产代码,请使用 libuv、libevent 或 Boost.Asio;它们可以抽象平台差异,并提供经过测试的 I/O 循环。
异步 I/O 与线程
异步 I/O 更具扩展性,但编写起来更困难。对于连接数较少的千级服务器,为每个连接创建一个线程的模型更简单。请根据预期负载进行选择。
快速检查
epoll 中的边沿触发模式与水平触发模式有什么区别?
回顾
异步 I/O 通过让一个线程处理多个套接字,扩展到大规模并发。Linux 使用 epoll,BSD/macOS 使用 kqueue,Windows 使用 IOCP。请将套接字设置为非阻塞模式。在生产环境中,请使用库(Asio、libuv、libevent),而不是手动编写事件循环。
常见问题解答
「使用 epoll 和 kqueue 进行异步 IO」课时是免费的吗?
是的 — 「使用 epoll 和 kqueue 进行异步 IO」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C++ Academy 课程的其余内容,请升级到 CoddyKit PRO。 C++ Academy 课程共包含 4 节课。
「使用 epoll 和 kqueue 进行异步 IO」这节课中我会学到什么?
在 Linux 上使用 epoll、在 BSD 或 macOS 上使用 kqueue,扩展到处理大量连接 你通过在浏览器中直接运行的动手代码来练习 C++ Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C++ Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C++ Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「使用 epoll 和 kqueue 进行异步 IO」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C++ Academy 课中编写并运行代码吗?
能。每节 C++ Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- C++ 中的 BSD 套接字 API
- 构建 TCP 回显服务器
- 使用 epoll 和 kqueue 进行异步 IO
- 使用 Boost Asio 实现现代异步 IO