当前位置: 首页 > ds >正文

I/O 多路复用实现方式

I/O 多路复用(I/O Multiplexing)的实现方式

I/O 多路复用是一种 通过单个线程监控多个 I/O 事件(如可读、可写) 的技术,核心目标是 用最少的资源管理大量并发连接。以下是主要的实现方式及其对比:


1. 主要实现方式

(1) select

特点
  • 跨平台支持:几乎在所有操作系统(Linux/Windows/macOS)上可用。
  • 基于位图(fd_set):监听的文件描述符(fd)数量有限(通常 1024)。
  • 线性扫描:每次调用需遍历所有 fd,时间复杂度 O(n)。
  • 触发方式:水平触发(LT,Level-Triggered)。
代码示例(C)
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(sockfd, &read_fds);struct timeval timeout = {5, 0}; // 5秒超时
int ret = select(sockfd + 1, &read_fds, NULL, NULL, &timeout);
if (ret > 0 && FD_ISSET(sockfd, &read_fds)) {// 可读事件就绪
}
缺点
  • 效率低(高并发时遍历开销大)。
  • 无法动态扩展 fd 数量。

(2) poll

特点
  • 改进 select 的 fd 数量限制:使用链表存储 fd,理论无上限(但性能仍受限制)。
  • 仍为线性扫描:时间复杂度 O(n)。
  • 触发方式:水平触发(LT)。
代码示例(C)
struct pollfd fds[1];
fds[0].fd = sockfd;
fds[0].events = POLLIN; // 监听可读事件int ret = poll(fds, 1, 5000); // 5秒超时
if (ret > 0 && (fds[0].revents & POLLIN)) {// 可读事件就绪
}
缺点
  • select 一样有遍历开销,性能不高。

(3) epoll(Linux 专属)

特点
  • 高效事件通知:基于事件回调,仅返回就绪的 fd,时间复杂度 O(1)。
  • 支持高并发:可管理数十万连接。
  • 触发方式
    • 水平触发(LT,默认):只要 fd 可读/可写,就会重复通知。
    • 边缘触发(ET):仅在状态变化时通知一次(需一次性处理完数据)。
  • 核心函数
    • epoll_create():创建 epoll 实例。
    • epoll_ctl():注册/修改/删除 fd 监听事件。
    • epoll_wait():等待事件就绪。
代码示例(C)
int epfd = epoll_create1(0);
struct epoll_event ev, events[10];
ev.events = EPOLLIN | EPOLLET; // 监听可读 + 边缘触发
ev.data.fd = sockfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);while (1) {int n = epoll_wait(epfd, events, 10, 5000); // 5秒超时for (int i = 0; i < n; i++) {if (events[i].data.fd == sockfd) {// 处理可读事件(ET 模式需循环 read() 直到 EAGAIN)}}
}
优点
  • 高性能,适合高并发场景(如 Nginx、Redis)。
  • 支持边缘触发(ET),减少事件重复通知。
缺点
  • 仅限 Linux 系统。

(4) kqueue(FreeBSD/macOS 专属)

特点
  • 类似 epoll,但用于 BSD/macOS 系统。
  • 支持更多事件类型(如文件系统事件)。
  • 核心函数
    • kqueue():创建 kqueue 实例。
    • kevent():注册/等待事件。
代码示例(C)
int kq = kqueue();
struct kevent ev, events[10];
EV_SET(&ev, sockfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
kevent(kq, &ev, 1, NULL, 0, NULL);while (1) {int n = kevent(kq, NULL, 0, events, 10, &(struct timespec){5, 0}); // 5秒超时for (int i = 0; i < n; i++) {if (events[i].ident == sockfd) {// 处理可读事件}}
}
优点
  • 高性能,功能比 epoll 更丰富。
缺点
  • 仅限 BSD/macOS。

(5) IOCP(Windows 专属)

特点
  • 异步 I/O 模型:与 epoll/kqueue 不同,IOCP 是真正的异步 I/O(非事件驱动)。
  • 核心函数
    • CreateIoCompletionPort():创建 IOCP 实例。
    • GetQueuedCompletionStatus():获取已完成的操作。
适用场景
  • Windows 高性能服务器(如 IIS)。

2. 对比总结

实现方式操作系统时间复杂度最大 fd 数量触发模式特点
select跨平台O(n)1024(默认)LT兼容性好,效率低
poll跨平台O(n)无硬限制LT改进 select 的 fd 数量限制
epollLinuxO(1)数十万LT/ET高性能,边缘触发优化
kqueueFreeBSD/macOSO(1)数十万LT/ET功能丰富,类似 epoll
IOCPWindows异步回调无硬限制完成通知(非事件)真正的异步 I/O

3. 如何选择?

  • Linux 服务器:优先用 epoll(Nginx、Redis、Netty)。
  • BSD/macOS:用 kqueue
  • Windows:用 IOCP
  • 跨平台需求
    • 低并发 → select/poll
    • 高并发 → 封装不同系统的多路复用(如 Libevent、Libuv)。

4. 常见问题

Q:为什么 epollselect/poll 快?

  • select/poll 每次调用需遍历所有 fd,而 epoll 仅返回就绪的 fd。
  • epoll 使用内核事件表(红黑树 + 就绪链表),避免重复拷贝 fd 集合。

Q:水平触发(LT) vs 边缘触发(ET)?

  • LT:只要 fd 可读/可写,会重复通知(编程简单,但可能重复触发)。
  • ET:仅在状态变化时通知一次(需一次性处理完数据,性能更高)。

Q:epoll 的惊群问题?

  • 多个线程/进程监听同一 epoll,事件可能被所有线程唤醒(Linux 4.5+ 已修复)。

5. 现代应用

  • Nginxepoll(Linux)/kqueue(BSD)。
  • Redis:单线程 epoll
  • Netty:封装 epoll/kqueue 实现跨平台 Reactor 模式。
http://www.xdnf.cn/news/15522.html

相关文章:

  • kafka的部署
  • 第十二批深度合成算法备案情况
  • 分布式系统中设计临时节点授权的自动化安全审计
  • Codeforces Round 787 (Div. 3)(A,B,C,D,E,F,G)
  • NSSCTF Web 一点学习
  • 【2025/07/14】GitHub 今日热门项目
  • 015 程序地址空间入门
  • muduo面试准备
  • ThreadLocal深度解析:结构、存储机制与最佳实践
  • Linux ACL权限策略
  • 分享三个python爬虫案例
  • Docker搭建Redis分片集群
  • 【PTA数据结构 | C语言版】字符串连接操作
  • Kotlin集合接口
  • 【数据同化案例1】ETKF求解参数-状态联合估计的同化系统(完整MATLAB实现)
  • 问题记录:Fastjson序列化-空值字段处理
  • 跨域中间件通俗理解
  • 日记-生活随想
  • LVS负载均衡集群概述
  • C++--List的模拟实现
  • 【时时三省】(C语言基础)通过指针引用数组元素2
  • 20250711_Sudo 靶机复盘
  • 【读书笔记】《Effective Modern C++》第4章 Smart Pointers
  • 串口学习和蓝牙通信HC05(第八天)
  • es里的node和shard是一一对应的关系吗,可以多个shard分配到一个node上吗
  • Pandas-数据清洗与处理
  • 构建可落地的企业AI Agent,背后隐藏着怎样的技术密码?
  • redis汇总笔记
  • 什么时候需要用到 multiprocessing?
  • 基于 CentOS 7 的 LVS+DR+Web+NFS 旅游攻略分享平台部署