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

OS--常见的网络模型(包含IO多路复用的原理)

网络模型

在这里插入图片描述

IO模型主要就是用户空间和内核空间数据交换的形式。

IO模型

  1. 阻塞 I/O 模型(Blocking I/O)
    • 应用程序发起 I/O 请求后,会被阻塞,直到 I/O 操作完成。
  2. 非阻塞 I/O 模型(Non-blocking I/O)
    • 应用程序发起 I/O 请求后,不会阻塞,可以立即返回。如果 I/O 操作未完成,应用程序需要不断轮询检查 I/O 操作的状态。
  3. I/O 复用模型(I/O Multiplexing)
    • 通过系统调用如 selectpollepoll,应用程序可以同时监视多个 I/O 通道,当任何一个通道有数据可读或可写时,系统调用返回。
  4. 信号驱动 I/O 模型(Signal-Driven I/O)
    • 应用程序发起 I/O 请求后,可以继续执行其他任务。当 I/O 操作完成时,操作系统会发送一个信号给应用程序,通知其 I/O 操作已经完成。
  5. 异步 I/O 模型(Asynchronous I/O)
    • 应用程序发起 I/O 请求后,可以立即返回并继续执行其他任务。I/O 操作由操作系统在后台完成,完成后操作系统会通知应用程序。

阻塞IO

阻塞IO就是两个阶段都必须阻塞等待。

在这里插入图片描述

非阻塞IO

非阻塞IO的recvfrom操作会立即返回结果而不是阻塞用户进程。

在这里插入图片描述

虽然是非阻塞,但是性能并没有提升。而且忙等机制会导致CPU空转,CPU使用率暴增。

IO多路复用

比如服务器处理客户端Socket请求时,在单线程情况下,只能依次处理一个socket,如果正在处理的socket恰好未就绪(数据不可读或不可写),线程就会被阻塞,所有其他的线程客户端socket都必须等,性能自然会差。

如何提高效率呢?

  • 方案一:使用多线程进行处理。
  • 方案二:数据就绪了,用户就去读取数据。

那么如何知道内核中的数据就绪了?

文件描述符(File Descriptor) :简称FD,是一个从0开始递增的无符号整数,用来关联Linux中的一个文件。在Linux中,一切皆文件,例如常规文件、视频、硬件设备等,当然也包括网络套接字(Socket)。

IO多路复用 : 是利用单个线程来同时监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。

在这里插入图片描述

recvfrom 只能监听一个fd,而select可以一次性传多个fd,效率比 recvfrom 高。

监听FD的方式、通知的方式又有多种实现,常见有:

  • select
  • poll
  • epoll

差异:

  • select和poll只会通知用户进程有FD就绪,但不确定具体是哪个FD,需要用户进程逐个遍历FD来确认
  • epoll则会在通知用户进程FD就绪的同时,把已就绪的FD写入用户空间

select

select是Linux最早是由的I/O多路复用技术:

简单说,就是我们把需要处理的数据封装成FD,然后在用户态时创建一个fd的集合(这个集合的大小是要监听的那个FD的最大值+1,但是大小整体是有限制的 ),这个集合的长度大小是有限制的,同时在这个集合中,标明出来我们要控制哪些数据,

比如要监听的数据,是1,2,5三个数据,此时会执行select函数,然后将整个fd发给内核态,内核态会去遍历用户态传递过来的数据,如果发现这里边都数据都没有就绪,就休眠,直到有数据准备好时,就会被唤醒,唤醒之后,再次遍历一遍,看看谁准备好了,然后再将处理掉没有准备好的数据,最后再将这个FD集合写回到用户态中去,此时用户态就知道了,奥,有人准备好了,但是对于用户态而言,并不知道谁处理好了,所以用户态也需要去进行遍历,然后找到对应准备好数据的节点,再去发起读请求,我们会发现,这种模式下他虽然比阻塞IO和非阻塞IO好,但是依然有些麻烦的事情, 比如说频繁的传递fd集合,频繁的去遍历FD等问题

在这里插入图片描述

select模式存在的三个问题:

  • 能监听的FD最大不能超过1024
  • 发生2次「拷贝」文件描述符集合,先从用户空间传入内核空间,由内核修改后,再传出到用户空间中。
  • 每次都要遍历所有FD来判断就绪装填

poll

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

poll模式问题:

  • 使用「线性结构」存储进程关注的 Socket 集合,因此都需要遍历文件描述符集合来找到可读或可写的 Socket,时间复杂度为 O(n),而且也需要在用户态与内核态之间拷贝文件描述符集合,这种方式随着并发数上来,性能的损耗会呈指数级增长。

epoll

先用epoll_create 创建一个 epol l对象 epfd,再通过 epoll_ctl 将需要监视的 socket 添加到epfd中,最后调用 epoll_wait 等待数据。
在这里插入图片描述

epoll 通过两个方面,很好解决了 select/poll 的问题。

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

在这里插入图片描述

epoll 的 边缘触发和水平触发有什么区别?

  • 使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完。
  • 使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取。

信号驱动IO

信号驱动IO是与内核建立SIGIO的信号关联并设置回调,当内核有FD就绪时,会发出SIGIO信号通知用户,期间用户应用可以执行其它业务,无需阻塞等待。

在这里插入图片描述

当有大量I0操作时,信号较多,SIGIO处理函数不能及时处理可能导致信号队列溢出

而且内核空间与用户空间的频繁信号交互性能也较低。

异步IO

在这里插入图片描述

http://www.lryc.cn/news/519133.html

相关文章:

  • LCE(Local Cascade Ensemble)预测模型和LSTM(Long Short-Term Memory)模型在效果和特点上存在显著差异
  • 【mysql】约束的基本使用
  • EasyExcel(二)导出Excel表自动换行和样式设置
  • 农产品直播带货方案拆解
  • “**H5**” 和 “**响应式**” 是前端开发中常见的术语,但它们的概念和使用场景有所不同
  • 基于EasyExcel实现通用版一对一、一对多、多层嵌套结构数据导出并支持自动合并单元格
  • Java堆内存分析
  • maven高级(day15)
  • 计算机组成原理(九):乘法器
  • python【输入和输出】
  • 2024年华为OD机试真题-判断一组不等式是否满足约束并输出最大差-Python-OD统一考试(E卷)
  • 【json】
  • 基于单片机的无线智能窗帘控制器的设计
  • 磁盘满造成业务异常问题排查
  • C++例程:使用I/O模拟IIC接口(6)
  • 58.在 Vue 3 中使用 OpenLayers 绘制点、线、圆、多边形
  • 如何快速上手一个鸿蒙工程
  • c++入门之 命名空间与输入输出
  • GRE技术的详细解释
  • Mysql--基础篇--多表查询(JOIN,笛卡尔积)
  • Java 泛型的用法
  • 人工智能与物联网:智慧城市的未来
  • Python标准库之SQLite3
  • 力扣 二叉树的最大深度
  • Linux_进程间通信_共享内存
  • ubuntu 下生成 core dump
  • 学习HLS.js
  • 2025年华为OD上机考试真题(Java)——判断输入考勤信息能否获得出勤奖
  • 空对象模式
  • 开启Excel导航仪,跨表跳转不迷路-Excel易用宝