“IO 多路复用”是编程中常见的技术词汇,使用这种技术的框架有很多,如,Redis、Kafka、Netty、Nginx 中都用到了此技术。那问题来了,什么是 IO 多路复用?它的具体实现技术有哪些?这些技术之间有什么区别?今天我们就来简单的探讨一下。
1.什么是IO多路复用?
IO 多路复用技术是一种允许单个线程管理多个网络连接的技术,它使得服务器能够高效地处理大量的并发连接而不需要为每个连接创建一个独立的线程或进程。
图片
想象如果客户端有成千上万个的情况下,那么非 IO 多路复用就会有成千上万个线程,那么就会发生 IO 过度争抢和多线程切换的问题,因为 CPU 资源只有几个,而要执行的线程却有成千上万个。
2.IO多路复用技术实现
常用的 IO 多路复用实现技术有:select、poll、epoll 和 kqueue 等,它们的具体介绍如下。
2.1 select
- 特点:select 是最早出现的一种多路复用 I/O 模型,几乎在所有平台上都有支持。它通过一个调用来监视多个文件描述符,等待其中任何一个变为可读或可写状态。
- 局限性:
文件描述符数量受限:通常限制为 1024 个,可以通过修改系统参数来增加这个限制,但这样做会消耗更多的系统资源。
效率低下:每次调用 select 都需要将文件描述符列表复制到内核,检查完后又需要复制回用户空间,这对于大量文件描述符来说效率很低。
不支持边缘触发模式:只支持水平触发模式。
边缘触发模式 VS 水平触发模式
- 边缘触发模式:当一个文件描述符从不可读(或不可写)变为可读(或可写)时,内核仅通知应用程序一次。如果应用程序未能立即处理完所有可用的数据(例如,缓冲区中的数据未完全读取),那么即使文件描述符仍然是可读的,内核也不会再次通知应用程序,直到该文件描述符的状态再次发生变化(例如,从可读变为不可读,再变回可读)。
优点:减少了系统调用次数,提高了效率,特别适合于大数据量传输的场景。
缺点:要求应用程序必须在接收到事件后尽可能多地读取或写入数据,否则可能会错过后续的数据。因此,边缘触发模式对编程的要求较高,需要更加小心地处理。
- 水平触发模式:在水平触发模式下,只要文件描述符处于可读(或可写)状态,无论之前是否已经通知过,内核都会持续通知应用程序。这意味着,如果应用程序未能一次性处理完所有数据,只要文件描述符仍然处于可读或可写状态,内核就会继续发送通知。
- 优点:编程较为简单,因为即使错过了某个事件的通知,只要文件描述符的状态没有改变,应用程序仍然有机会在下一次轮询时接收到同样的事件。
- 缺点:可能造成更多的系统调用,因为即使数据已经被部分处理,内核仍然会不断地通知应用程序,这可能导致效率降低。
2.2 poll
- 特点:poll 在功能上与 select 非常相似,但没有文件描述符数量的限制。poll 使用一个 pollfd 结构体数组来表示要监听的文件描述符集合。
- 局限性:虽然解决了 select 的文件描述符数量限制问题,但在性能上仍然存在类似 select 的问题,即每次调用都需要复制文件描述符列表到内核,并且返回时也需要复制回用户空间。
2.3 epoll
- 特点:epoll 是 Linux 特有的高效 IO 多路复用技术,它克服了 select 和 poll 的所有缺点。epoll 使用三个系统调用来管理文件描述符:epoll_create 创建一个 epoll 实例,epoll_ctl 添加/删除需要监听的文件描述符,epoll_wait 等待事件的发生。
- 优势:
无数量限制:没有文件描述符数量限制。
高效:只有活跃的文件描述符才会被传递给用户空间,减少了不必要的复制操作。
功能强大:支持边缘触发和水平触发两种工作模式。
2.4 kqueue
- 特点:kqueue 是 FreeBSD 操作系统引入的一种 IO 多路复用技术,后来也被 Mac OS X 和其他基于 BSD 的操作系统采用。kqueue 可以同时处理多种类型的事件,包括但不限于文件描述符事件、信号事件等。
- 优势:
功能更强大:不仅支持文件描述符的事件通知,还能处理其他类型的事件。
性能优秀:与 epoll 类似,只有活跃的文件描述符才会被处理,从而提高了效率。
3.区别对比
select、poll、epoll 和 kqueue 之间的区别如下:
技术名称 | 支持平台 | 连接数限制 | IO效率 | 数据拷贝方式 |
select | 跨平台 | 默认1024 | O(N) | 每次调用都拷贝 |
poll | 跨平台 | 无 | O(N) | 每次调用都拷贝 |
epoll | Linux 特有 | 无 | O(1) | 仅在 epoll_ctl 时拷贝 |
kqueue | MacOS、FreeBSD 等 | 无 | O(1) | 具体实现方式可能因系统而异,但通常也是高效的。 |
课后思考
什么叫做“文件描述符”?IO 多路复用为什么要进行“数据拷贝”?