Linux之【网络I/O】前世今生(二)

前文回顾

在这里插入图片描述

通过学习 Linux之【网络I/O】前世今生(一),我们知道了I/O 请求可以分为两个阶段,分别为 I/O 调用和 I/O 执行:

  • I/O 调用

即用户进程向内核发起系统调用(通过 0x80 中断)。

  • I/O 执行

内核等待 I/O 请求处理完成返回。该阶段分为两个过程:

  1. 等待数据就绪,并写入内核缓冲区;
  2. 将内核缓冲区数据拷贝至用户态缓冲区。

一、 网络 I/O 模型

在 Linux之【磁盘 IO】前世今生 一文中,我们提到了 I/O的三种模型:同步阻塞I/O (BIO)、同步非阻塞I/O (NIO)、异步I/O (AIO)。

对于网络I/O,我们是否可以采用上述I/O模型呢 ?

网络I/O 会涉及 Socket ,且同一时间可能有大量 Socket 连接和读写操作,需要考虑并发量和性能……

  • 首先可以排除异步I/O,因为Linux 异步I/O最初是为数据库设计的;
  • 同步阻塞I/O (BIO):考虑并发情况的话,只能使用多线程模型,一个请求对应一个线程。但是,线程的资源是有限且宝贵的,创建过多的线程会增加线程切换的开销。
    • 请求数只有 100 个时,这种方式自然没问题,但增加到 10000 个请求时,10000 个进程或线程的调度、上下文切换乃至它们占用的内存,都会成为瓶颈。
  • 同步非阻塞I/O (NIO):NIO 相比 BIO 虽然大幅提升了性能,但是轮询过程中大量的系统调用导致上下文切换开销很大。所以,单独使用 NIO 时效率并不高,而且随着并发量的提升,非阻塞 I/O 会存在严重的性能浪费。

1.1 I/O多路复用(I/O Multiplexing)

针对上文分析结果,同时结合网络I/O 的并发量考虑,我们改进下 NIO 模型:

  • NIO 轮询产生的多次上下文切换,我们可以减少,正如我们我们之前用 sendfile 替代 [ read + write ] 一样,我们把轮询工作放在内核态进行;
  • 所有 Socket 都注册到内核里,由内核统一负责监听所有Socket的I/O数据是否就绪;
  • 线程资源有限且宝贵,我们通过减少线程数量来降低线程切换开销,一个用户线程负责处理多个Socket;
  • 任一Socket 的 I/O 就绪,内核就通知应用程序;

我们用 多路复用器(Selector)来封装上述改进结果,即通过一次系统调用,检查多个Socket的状态,将这种改进后 I/O 模型记为I/O多路复用(I/O Multiplexing)

  • 多路复用实现了一个线程处理多个 I/O 句柄的操作。多路指的是多个数据通道,复用指的是使用一个或多个固定线程来处理每一个 Socket。线程一次 select 调用可以获取内核态中多个数据通道的数据状态。多路复用解决了同步阻塞 I/O 和同步非阻塞 I/O 的问题,是一种非常高效的 I/O 模型。
  • I/O 多路复用内部实现需要使用非阻塞 I/O
    • 多路复用内部需要遍历 Socket 集合,如果使用阻塞I/O,当前遍历 Socket 数据未就绪会阻塞,那么后续 Socket 将没有机会遍历检查,直到当前阻塞解除;
    • 注意:这里说的是 I/O 多路复用内部实现,而不是说,使用 I/O 多路复用就必须使用非阻塞 I/O。

在这里插入图片描述

Linux中 I/O 多路复用的具体实现有:select、poll、epoll ;kueue 是在UNIX上比较高效的IO复用技术。

1.1.1 select

select 允许应用程序监视一组 Socket 文件描述符,等待一个或者多个描述符成为就绪状态,从而完成 I/O 操作。调用 select 会一直阻塞直到有描述符的事件到达或者等待超时(timeout 参数精度为微秒)。

具体实现方式如下:
  1. 将已连接的 Socket 文件描述符放到一个 BitsMap 数组,然后调用 select 函数将 BitsMap 数组拷贝到内核里;
  2. 内核通过遍历BitsMap 数组,来检查是否有网络事件产生,当检查到有事件产生后,将此 Socket 标记为可读或可写;
  3. BitsMap 数组从内核拷贝到用户态;
  4. 用户态遍历 BitsMap 找到可读写的Socket,然后再对其处理。
int select(int nfds,   // 需要监听的 socket (也叫文件描述符(file descriptor))数量; 
         fd_set *restrict readfds, // 可读集合
         fd_set *restrict writefds,// 可写集合
         fd_set *restrict errorfds,// 异常集合

		 // 超时设置,NULL,会无限阻塞直到某个描述符就绪;如果 timeout 参数设为 0,会立即返回,不阻塞。
         struct timeval *restrict timeout);
  • 数组遍历时间复杂度为O(n);
  • 每次调用BitsMap需要拷贝2次,高并发场景下这样的拷贝消耗的资源是惊人的;
  • BitsMap 长度由参数 FD_SETSIZE 限制,默认最大值为1024,即只能监听0~1023的文件描述符;
应用场景
  • select 的 timeout 参数精度为微秒,因此 select 更加适用于实时性要求比较高的场景;
  • select 可移植性更好,几乎被所有主流平台所支持;
  • 同时监控小于 1000 个描述符。

1.1.2 poll

为了摆脱select文件描述符最大个数的限制(当然还会受到系统文件描述符限制),poll 在内核态将文件描述符数组改为链表,链表节点内容基于pollfd

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

struct pollfd {
    int fd;           // 要监听的文件描述符
    short events;     // 要监听的事件 读事件、写事件、异常事件
    short revents;    // 文件描述符fd上实际发生的事件
};
  • 链表遍历时间复杂度为O(n);
  • poll 提供了更多的事件类型,并且对描述符的重复利用上比 select 高;
  • select 会修改描述符,而 poll 不会。
应用场景

poll 的 timeout 参数精度为毫秒,且poll 没有最大描述符数量的限制,如果平台支持并且对实时性要求不高,可以使用 poll 而不是 select。

1.1.3 epoll

Linux 2.6 新增的 epoll(eventpoll,事件驱动的 poll) 是对 select 和 poll 的改进,解决了 “全量复制集合” 和 “文件描述符数量少” 这两个缺点,是性能最高的多路复用实现方式,能支持的并发量也是最大。

kueue 是 unix (FreeBSD,NetBSD, OpenBSD, DragonFly BSD, macOS)中用来替换select和poll的。

改进点如下:
  • 内核使用红黑树存储所有需要监听的 Socket 连接,每个Socket 连接只需在添加(通过系统调用epoll_ctl)时传入一次,无需用户每次都重新传入;

  • 内核用链表存储就绪的 Socket 连接;这样用户态只需处理该链表即可,无需遍历整个Socket集合;

  • 内核采用事件驱动机制来监听Socket是否就绪,避免轮询扫描全部 Socket 连接;

// 创建一个 epoll 实例,同时返回一个引用该实例的文件描述符。
int epoll_create(int size);

// 监听文件描述符 fd 上发生的 event 事件。op 表示要对 fd 执行的操作,添加,删除、更新
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 

// 功能相当于 select/poll。
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
               
struct eventpoll {
 wait_queue_head_t wq;      // 等待队列链表,存放阻塞的进程
 struct list_head rdllist;  // 存放数据就绪的 socktet
 struct rb_root rbr;        // redBlackTree 红黑树,管理用户进程下添加进来的所有 socket 连接
        ......
}

在这里插入图片描述

  • 红黑树查找,时间复杂度为O(log n);
应用场景
  • epoll 的 timeout 参数精度为毫秒,有大量的描述符需要同时轮询,并且这些连接是长连接、不活跃;
  • C10K、C100K、C1000K:即单机同时处理 1 万(十万、百万)个请求(并发连接 1 万、10万、100万)的问题,首字母 C 是 Client 或者Concurrent的缩写。再往上走到 C10M问题 用 epoll 已经搞不定了?得用用户态网络协议栈,扯远了……

1.1.4 select vs poll vs epoll

项目selectpollepoll
socket 数据就绪复制数量全部复制(包含没有就绪的)全部复制(包含没有就绪的)仅复制数据就绪集合
socket集合复制次数每次调用和返回均需复制每次调用和返回均需复制仅第一次调用复制,后续返回仅复制少量数据
集合大小有限制没有限制没有限制
数据就绪机制遍历遍历事件驱动
集合底层结构数组链表红黑树
时间复杂度O (n)O (n)O (log n)
阻塞超时精度微秒毫秒毫秒
使用场景实时性要求比较高,并发低于1000实时性要求不高,并发稍微高于1000海量并发,长连接、不活跃

1.1.5 I/O 事件通知的方式

  • 水平触发(LT,Level Trigger):当数据就绪时,会触发通知,如果用户程序没有一次性把数据读/写完,下次还会发出可读/可写信号进行通知。

  • 边缘触发(ET,Edge Trigger):仅当数据从未就绪变为就绪时,通知一次,之后不会再通知。

    边缘触发时,应用程序需要尽可能多地执行 I/O,直到无法继续读写,才可以停止。如果 I/O 没执行完,或者因为某种原因没来得及处理,那么这次通知也就丢失了。

  • 水平触发、边缘触发的名称来源:数字电路当中的电位水平,高低电平切换瞬间的触发动作叫边缘触发,而处于高电平的触发动作叫做水平触发。
  • 区别:边缘触发效率更高,减少了事件被重复触发的次数,函数不会返回大量用户程序可能不需要的文件描述符。
  • select 、poll 只支持水平触发;
  • epoll 支持水平触发和边缘触发;

1.2 信号I/O

信号驱动 I/O 并不常用,它是一种半异步的 I/O 模型。在使用信号驱动 I/O 时,当数据准备就绪后,内核通过发送一个 SIGIO 信号通知应用进程,应用进程就可以开始读取数据了。

在这里插入图片描述

信号I/O和异步I/O区别:

  • 信号I/O,内核只负责发信号通知用户态,有没有数据;即信号在数据就绪时发送;
  • 异步I/O,内核会帮助用户态拷贝数据;即在数据拷贝完成时通知用户。

二、 I/O模型总结对比

在 Linux之【磁盘 IO】前世今生 我们介绍了阻塞I/O 、非阻塞I/O和异步I/O,本文我们梳理了I/O多路复用和信号I/O,至此,I/O 5 种模型已介绍完毕,对比如下:

在这里插入图片描述

Linux 前世今生系列文章:

  • Linux之【网络I/O】前世今生(二)

  • Linux之【网络I/O】前世今生(一)

  • Linux之【磁盘 IO】前世今生

  • Linux之【文件系统】前世今生(二)

  • Linux之【文件系统】前世今生(一)

  • Linux之【内存管理】前世今生(一)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/970093.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis未授权访问漏洞导致getshell

一、漏洞信息 redis默认情况下会绑定在本地6379端口,如果没有进行采用相关的策略,就会将redis服务暴露到公网上,如果再没有设置密码认证(一般为空)的情况下,会导致任意用户可以访问到目标服务器的情况下未授权访问redis以及读取r…

伯克利 CS61A 课堂笔记 08 —— Strings and Dictionaries

本系列为加州伯克利大学著名 Python 基础课程 CS61A 的课堂笔记整理,全英文内容,文末附词汇解释。 目录 01 Strings 字符串 Ⅰ Strings are An Abstraction. Ⅱ Strings Literals have Three Forms Ⅲ String are Sequences 02 Dictionaries 字典 …

【Stable Diffusion模型测试】测试ControlNet,没有线稿图?

相信很多小伙伴跟我一样,在测试Stable Diffusion的Lora模型时,ControlNet没有可输入的线稿图,大家的第一反应就是百度搜,但是能从互联网上搜到的高质量线稿图,要么收费,要么质量很差。 现在都什么年代了&a…

智能手表表带圆孔同心度检测

在智能手表的制造工艺中,表带圆孔同心度检测是确保产品品质的关键环节。精准的同心度不仅关乎表带与表体的完美适配,更直接影响用户的佩戴舒适度和产品的整体美观度。稍有偏差,就可能导致表带安装困难、佩戴时出现晃动,甚至影响智…

基于SSM+uniapp的数学辅导小程序+LW示例参考

1.项目介绍 系统角色:管理员、普通用户功能模块:用户管理、学习中心、知识分类管理、学习周报管理、口算练习管理、试题管理、考试管理、错题本等技术选型:SSM,Vue(后端管理web),uniapp等测试环…

基于 openEuler 构建 LVS-DR 群集

一、 对比 LVS 负载均衡群集的 NAT 模式和 DR 模式,比较其各自的优势 。 二、 基于 openEuler 构建 LVS-DR 群集。 一 NAT 模式 部署简单:NAT 模式下,所有的服务器节点只需要连接到同一个局域网内,通过负载均衡器进行网络地址转…

JS设计模式之单例原型

那么单例模式都有哪些应用场景呢?如何通过构造函数创建单例如何使用模块化的方式创建总结 各位老铁们,今天我们介绍一下JS中单例设计模式,它的特点是确保一个类只有一个实例,并提供一个全局访问点来获取该实例(无论被创…

vue+springboot+webtrc+websocket实现双人音视频通话会议

前言 最近一些时间我有研究,如何实现一个视频会议功能,但是找了好多资料都不太理想,最终参考了一个文章 WebRTC实现双端音视频聊天(Vue3 SpringBoot) 只不过,它的实现效果里面只会播放本地的mp4视频文件&…

Linux 基础IO——重定向和缓冲区

目录 一、重定向 1、重定向的本质 2、使用 dup2 系统调用 (1)输出重定向 (2)追加重定向 (3) 输入重定向 ​ 二、缓冲区 1.理解缓冲区 2.缓冲区刷新问题 3.为什么要有缓冲区? 4.这个缓冲区在哪里&#xff…

14、deepseek视觉大模型Janus Pro本地部署及实战

1、简介 2025.01.27: Janus-Pro发布,Janus的高级版本,显著提高了多模态理解和视觉生成。 Janus-Pro 是 Janus 的高级版本。具体来说, Janus-Pro 包括以下改进:优化的训练策略、 扩展的训练数据以及更大规模的模型。通…

【第3章:卷积神经网络(CNN)——3.1 CNN的基本结构与工作原理】

嘿,小伙伴们,今天咱们来聊聊深度学习里的一大明星——卷积神经网络(CNN)。这东西在图像识别、视频处理等领域简直不要太火,甚至人脸识别、物体检测这些高大上的应用,都离不开它的身影。废话不多说,咱们这就开聊! 一、CNN是什么东东? 在人工智能领域,卷积神经网络(…

VMware Workstate 的 Ubuntu18 安装 vmware tools(不安装没法共享)

在共享主机路径后,可以在: /mnt/hgfs/下方找到共享的文件。但没有安装vmware tool时是没法共享的。 如何安装vmware tool,网上版本很多。这里记录一下: VMware Workstation 17 Pro,版本:17.6.0 虚拟机系统…

高效开发!使用Chrome对MoonBit生成的Wasm进行性能分析!

在 [我们前一篇博客][call-wasm-from-js] 中,我们介绍了如何在前端 JavaScript 中使用 MoonBit 驱动的 Wasm 库 [Cmark]。在本文中,我们将探索如何直接从 Chrome 浏览器中对该库进行性能分析。希望这篇教程能对你在使用 MoonBit 在类似的场景中进行开发时…

《安富莱嵌入式周报》第350期:Google开源Pebble智能手表,开源模块化机器人平台,开源万用表,支持10GHz HRTIM的单片机,开源CNC控制器

周报汇总地址:嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版: https://www.bilibili.com/video/BV1YPKEeyEeM/ 《安富莱嵌入式周报》第350期:Google开…

2.【BUUCTF】bestphp‘s revenge

进入题目页面如下 进行代码审计 <?php // 1. 高亮显示当前PHP文件的源代码&#xff0c;方便开发者查看代码内容&#xff0c;在生产环境中不应使用此函数&#xff0c;可能会导致代码泄露。 highlight_file(__FILE__);// 2. 定义变量 $b &#xff0c;其值为字符串 implode &…

HarmonyOS:使用List实现分组列表(包含粘性标题)

一、支持分组列表 在列表中支持数据的分组展示&#xff0c;可以使列表显示结构清晰&#xff0c;查找方便&#xff0c;从而提高使用效率。分组列表在实际应用中十分常见&#xff0c;如下图所示联系人列表。 联系人分组列表 在List组件中使用ListItemGroup对项目进行分组&#…

【vue3】入门基础知识点

Vue3核心语法 组合式API【vue3】与选项式API【vue2】 setup setup和data、methods同级别, 可与data等共存&#xff0c;data里面可以读取使用setup中声明的变量&#xff0c;而setup不能使用data中声明的变量&#xff08;setup加载时间早于beforeCreated&#xff09;setup中的…

DeepSeek官方发布R1模型推荐设置

今年以来&#xff0c;DeepSeek便在AI领域独占鳌头&#xff0c;热度一骑绝尘。其官方App更是创造了惊人纪录&#xff0c;成为史上最快突破3000万日活的应用&#xff0c;这一成绩无疑彰显了它在大众中的超高人气与强大吸引力。一时间&#xff0c;各大AI及云服务厂商纷纷投身其中&…

M3U8工作原理以及key解密视频流详解

文章目录 前言一、M3U8是什么&#xff1f;二、HLS—M3U8的工作原理1.分段视频流2.生成播放列表3.客户端请求和解析4.片段下载和播放 三、.m3u8文件内部是什么样的&#xff1f;四、简单介绍下AES-128算法五、拿到KEY后如何去解密&#xff1f;1.手动解密.ts文件2.前人栽树&#x…

重读《Java面试题,10万字208道Java经典面试题总结(附答案)》

最近重读了这篇文章&#xff0c;对很多概念模糊的地方加了拓展和补充。 目录 1、JDK 和 JRE 有什么区别&#xff1f; 2、 和 equals 的区别是什么&#xff1f; 3、final 在 java 中有什么作用&#xff1f; 4、java 中的 Math.round(-1.5) 等于多少&#xff1f; 5、String…