IO多路复用

简介: IO多路复用

9.2 I/O 多路复用:select/poll/epoll | 小林coding (xiaolincoding.com)

【并发】IO多路复用select/poll/epoll介绍_哔哩哔哩_bilibili 讲的有一点错误,但是可以理解

IO多路复用——深入浅出理解select、poll、epoll的实现 - 知乎 (zhihu.com)

原始的TCP连接

TCP Socket 基本只能一对一通信,因为使用的是同步阻塞的方式,当服务端在还没处理完一个客户端的网络 I/O 时,或者 读写操作发生阻塞时,其他客户端是无法与服务端连接的。

使用多进程优化

种用多个进程来应付多个客户端的方式,可以同时处理多个连接,但是也有很大的缺点:

  • 连接数量(开辟的进程)有限
  • 进程占用系统资源较多
  • 进程间上下文切换的代价很重的,性能会大打折扣。

使用多线程优化

同进程里的线程可以共享进程的部分资源,比如文件描述符列表、进程空间、代码、全局数据、堆、共享库等,这些共享些资源在上下文切换时不需要切换,而只需要切换线程的私有数据、寄存器等不共享的数据,因此同一个进程下的线程上下文切换的开销要比进程小得多。

  • 虽说线程切换的上写文开销不大,但是如果频繁创建和销毁线程,系统开销也是不小的
  • 如果使用线程池的方式来避免线程的频繁创建和销毁,会产生多线程竞争

IO多路复用是什么?

在linux系统中,实际上所有的I/O设备都被抽象为了文件这个概念,一切皆文件。磁盘、网络数据、终端,甚至进程间通信工具管道pipe等都被当做文件对待

一个进程虽然任一时刻只能处理一个请求,但是处理每个请求的事件时,耗时控制在 1 毫秒以内,这样 1 秒内就可以处理上千个请求,把时间拉长来看,多个请求复用了一个进程,这就是多路复用,这种思想很类似一个 CPU 并发多个进程,所以也叫做时分多路复用。

我们熟悉的 select/poll/epoll 内核提供给用户态的多路复用系统调用,进程可以通过一个系统调用函数从内核中获取多个事件

I/O模型

  • 阻塞IO 这是最常用的简单的IO模型。阻塞IO意味着当我们发起一次IO操作后一直等待成功或失败之后才返回,在这期间程序不能做其它的事情。 阻塞IO操作只能对单个文件描述符进行操作,比如readwrite
  • 非阻塞IO 进程发起IO系统调用后,如果内核缓冲区没有数据,需要到IO设备中读取,进程返回一个错误而不会被阻塞;进程发起IO系统调用后,如果内核缓冲区有数据,内核就会把数据返回进程。
  • 信号驱动IO 当进程发起一个IO操作,会向内核注册一个信号处理函数,然后进程返回不阻塞;当内核数据就绪时会发送一个信号给进程,进程便在信号处理函数中调用IO读取数据。
  • 异步IO 当进程发起一个IO操作,进程返回(不阻塞),但也不能返回果结;内核把整个IO处理完后,会通知进程结果。如果IO操作成功则进程直接获取到数据。
  • IO多路复用 使用一个进程来维护多个 Socket ,有 select/poll/epoll三种方法

Select

  1. 将已连接的 Socket 都放到一个文件描述符集合
  2. 调用 select 函数将文件描述符集合拷贝内核里,使用遍历的方式,让内核来检查是否有网络事件产生
  3. 当检查到有事件产生后,将此 Socket 标记为可读或可写
  4. 再把整个文件描述符集合拷贝用户态
  5. 然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理

select 使用固定长度的 BitsMap,表示文件描述符集合,而且所支持的文件描述符的个数是有限制的,在 Linux 系统中,由内核中的 FD_SETSIZE 限制, 默认最大值为 1024,只能监听 0~1023 的文件描述符。

小结

  • 位图BitsMap的大小有限,能开辟的文件描述符少
  • 需要遍历文件描述符集合来找到可读或可写的 Socket,时间复杂度为 O(n)
  • 需要在用户态与内核态之间拷贝文件描述符集合

Poll

poll 不再用 BitsMap 来存储所关注的文件描述符,取而代之用动态数组,以链表形式来组织,突破了 select 的文件描述符个数限制,当然还会受到系统文件描述符限制。

小结

  • 相比select ,突破文件描述符个数限制,当时还会受到系统文件描述符限制。
  • 需要遍历文件描述符集合来找到可读或可写的 Socket,时间复杂度为 O(n)
  • 需要在用户态与内核态之间拷贝文件描述符集合

Epoll

  • 先用 epoll_create 创建一个 epoll对象 epfd该函数生成一个 epoll 专用的文件描述符。其中 epoll_create 依靠 eventpoll 这个结构体实现,其中中的几个成员的含义如下:
  • wq: 等待队列链表。软中断数据就绪的时候会通过 wq 来找到阻塞在 epoll 对象上的用户进程。
  • rbr: 红黑树。为了支持对海量连接的高效查找、插入和删除,eventpoll 内部使用的就是红黑树。通过红黑树来管理用户主进程accept添加进来的所有 socket 连接。
  • rdllist: 就绪的描述符链表。当有连接就绪的时候,内核会把就绪的连接放到 rdllist 链表里。这样应用进程只需要判断链表就能找出就绪进程,而不用去遍历红黑树的所有节点了。
  • 再通过 epoll_ctl 将需要监视的 socket 添加到epfd中 epoll 的事件注册函数,一次性将所有需要监听的socket加入到内核中,后续可以避免再次复制的开销
  • 最后调用 epoll_wait 等待数据。 等待事件的产生,收集在 epoll 监控的事件中已经发送的事件,类似于 select() 调用。

Epoll 的优点

  • epoll 在内核里使用红黑树来跟踪进程所有待检测的文件描述字,把需要监控的 socket 通过 epoll_ctl() 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删改一般时间复杂度是 O(logn)减少了内核和用户空间大量的数据拷贝和内存分配。
  • epoll 使用事件驱动的机制,内核里维护了一个链表来记录就绪事件,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 epoll_wait() 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。

Epoll 边缘触发和水平触发

使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;

使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取;

举个例子,你的快递被放到了一个快递箱里,如果快递箱只会通过短信通知你一次,即使你一直没有去取,它也不会再发送第二条短信提醒你,这个方式就是边缘触发;如果快递箱发现你的快递没有被取出,它就会不停地发短信通知你,直到你取出了快递,它才消停,这个就是水平触发的方式。

  • 边缘触发:数据没处理完后续不会再触发事件
  • 水平触发:是不管数据有没有触发都返回事件

一般来说,边缘触发的效率比水平触发的效率要高,因为边缘触发可以减少 epoll_wait 的系统调用次数,系统调用也是有一定的开销的的,毕竟也存在上下文的切换。

小结

  • select: select允许程序同时监控多个文件描述符的读写状态,但受限于位图大小,且每次调用都需从用户空间向内核空间复制位图,性能开销大。
  • poll: poll改进了select,使用数组存储文件描述符,无位图大小限制,可处理更多文件描述符。但同样存在每次调用时的用户空间到内核空间的复制开销。
  • epoll: epoll是Linux特有的高效IO多路复用机制,基于事件驱动,无需轮询,通过注册感兴趣的事件并在事件发生时通知应用程序,适合处理大量并发连接,性能优越且资源消耗低。


相关文章
|
1月前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
89 0
|
6月前
|
存储 监控 Linux
【Linux IO多路复用 】 Linux下select函数全解析:驾驭I-O复用的高效之道
【Linux IO多路复用 】 Linux下select函数全解析:驾驭I-O复用的高效之道
1047 0
|
1月前
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
75 1
Linux C/C++之IO多路复用(aio)
|
1月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
23 0
Linux C/C++之IO多路复用(poll,epoll)
|
1月前
|
Java Linux
【网络】高并发场景处理:线程池和IO多路复用
【网络】高并发场景处理:线程池和IO多路复用
45 2
|
1月前
|
监控 网络协议 Java
IO 多路复用? 什么是 IO 多路复用? 简单示例(日常生活)来解释 IO 多路复用 一看就懂! 大白话,可爱式(傻瓜式)教学! 保你懂!
本文通过日常生活中的简单示例解释了IO多路复用的概念,即一个线程通过监控多个socket来处理多个客户端请求,提高了效率,同时介绍了Linux系统中的select、poll和epoll三种IO多路复用的API。
110 2
|
2月前
|
消息中间件 NoSQL Java
面试官:谈谈你对IO多路复用的理解?
面试官:谈谈你对IO多路复用的理解?
46 0
面试官:谈谈你对IO多路复用的理解?
|
2月前
|
网络协议 Java Linux
高并发编程必备知识IO多路复用技术select,poll讲解
高并发编程必备知识IO多路复用技术select,poll讲解
|
4月前
|
缓存 网络协议 算法
【Linux系统编程】深入剖析:四大IO模型机制与应用(阻塞、非阻塞、多路复用、信号驱动IO 全解读)
在Linux环境下,主要存在四种IO模型,它们分别是阻塞IO(Blocking IO)、非阻塞IO(Non-blocking IO)、IO多路复用(I/O Multiplexing)和异步IO(Asynchronous IO)。下面我将逐一介绍这些模型的定义:
206 2
|
4月前
|
存储 Java Unix
(八)Java网络编程之IO模型篇-内核Select、Poll、Epoll多路复用函数源码深度历险!
select/poll、epoll这些词汇相信诸位都不陌生,因为在Redis/Nginx/Netty等一些高性能技术栈的底层原理中,大家应该都见过它们的身影,接下来重点讲解这块内容。