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

【Linux网络】五种IO模型与阻塞IO

IO

在Linux网络环境里,IO(Input/Output)指的是网络数据在系统与外部网络(像其他设备、服务器或者客户端)之间进行传输的过程。

它是网络编程和系统性能优化的核心内容。

  • IO :INPUT和OUTPUT(站在进程角度)
  • 关于IO,read和write都是阻塞式的IO
  • 网络传输数据问题,本质上就是IO问题
  • 如何更好的理解IO问题,就需要更好的理解什么叫做高效的IO?
    • 首先明确,任何通信场景其IO通信场景,效率上一定是会有上限的 。
    • 抽象来说:IO=等+拷贝
    • 在拷贝的角度来说,我们需要充分利用硬件资源以及网络资源
    • 在“等”的角度来说,我们需要减少IO中“等”的权重

网络IO的核心流程

网络IO包含两个关键阶段:

  • 数据就绪:数据从网络传输到网卡,再由网卡传至内核缓冲区。
  • 数据拷贝:数据从内核缓冲区复制到用户空间的应用程序。

网络IO模型

Linux支持多种网络IO模型,它们的主要差异在于如何处理这两个阶段(阻塞、非阻塞、同步、异步)。

常见的网络IO模型有:

  • 阻塞IO(Blocking IO)
  • 非阻塞IO(Non-blocking IO)
  • IO多路复用(IO Multiplexing)
  • 信号驱动IO(Signal-driven IO)
  • 异步IO(Asynchronous IO)

注意:
前四种IO模型都属于同步IO,等的方式不同,但还是自己在深度参与IO过程,只要是自己参与了IO过程,就是同步IO

钓鱼例子

  • 张三:眼睛盯着鱼漂,鱼漂动了,钓鱼
    • 阻塞IO
  • 李四 :查看鱼漂是否动了,如果没有,就看手机刷抖音,期间不断查看鱼漂是否动了
    • 非阻塞IO(轮巡)
  • 王五 : 带来一车鱼竿插在岸边,来回检测哪一个鱼竿上的鱼漂动了
    • IO多路复用
  • 赵六 :在鱼漂上系上一个铃铛,开始钓鱼后刷抖音,直到铃铛响起,开始钓上鱼。
    • 信号驱动IO
  • 田七:我雇佣一个人给我钓鱼,钓到的鱼给我。
    • 异步IO

上述例子中:

  • 鱼竿:文件描述符
  • 钓鱼佬:进程

1. 阻塞IO(最基础的模型)

  • 工作方式:应用程序调用系统调用后会被阻塞,一直等到数据就绪并被复制到用户空间,或者出现错误时才会返回。所有的套接字,默认
    都是阻塞方式
  • 特点:编程逻辑简单,但同一时间只能处理一个连接,容易造成性能瓶颈。
    在这里插入图片描述

2. 非阻塞IO

  • 工作方式:应用程序调用系统调用后会立即返回。如果数据未就绪,会返回EWOULDBLOCK错误。应用程序需要不断轮询来检查数据是否就绪。
  • 特点:非阻塞IO能够避免线程长时间阻塞,但往往需要程序员循环的方式反复尝试读写文件描述符,这个过程称为轮询.这对CPU来说是较大的浪费,一般只有特定场景下才使用.
    在这里插入图片描述
3. IO多路复用(高性能服务器的核心)
  • 工作方式:借助selectpollepoll等系统调用,一个进程可以同时监视多个文件描述符(FD)。当某个FD的数据就绪时,就会通知应用程序进行处理。
  • 优势:能够用单线程处理大量并发连接,显著减少了系统开销。
    在这里插入图片描述

虽然从流程图上看起来和阻塞IO类似.实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态.

4. 信号驱动IO
  • 工作方式:应用程序先通过sigaction注册信号处理函数,然后继续执行其他任务。当数据就绪时,内核会发送SIGIO信号,应用程序在信号处理函数中进行数据读取操作。
  • 特点:属于异步通知模式,但数据拷贝阶段仍然是同步的。

在这里插入图片描述

5. 异步IO(真正的异步模型)
  • 工作方式:应用程序通过aio_read等接口发起异步IO请求,然后继续执行后续操作。内核会自动完成数据的读取和拷贝工作,完成后通过回调函数或者信号通知应用程序。
  • 特点:整个IO过程都是异步的,极大地提高了系统的并发处理能力。
    在这里插入图片描述
    小结:
  • 任何IO过程中,都包含两个步骤.第一是等待,第二是拷贝.
  • 而且在实际的应用场景中,等待消耗的时间往往都远远高于拷贝的时间.让IO更高效,最核心的办法就是让等待的时间尽量少

高级IO重要概念

在这里,我们要强调几个概念

同步通信 vs 异步通信(synchronous communication/ asynchronous communication)

同步和异步是消息通信机制。

  • 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了;换句话说,就是由调用者主动等待这个调用的结果
  • 异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果;换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果;而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

另外,我们回忆在讲多进程多线程的时候,也提到同步和互斥。这里的同步通信和进程之间的同步是完全不相干的概念。

  • 进程/线程同步也是进程/线程之间直接的制约关系。
  • 是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系。尤其是在访问临界资源的时候。

同学们以后在看到“同步”这个词,一定要先搞清楚大背景是什么。这个同步,是同步通信异步通信的同步,还是同步与互斥的同步。

阻塞 vs 非阻塞

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。

  • 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
  • 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

其他高级IO

非阻塞IO,纪录锁,系统V流机制,I/O多路转接(也叫I/O多路复用),readv和writev函数以及存储映射IO(mmap),这些统称为高级IO。

我们此处重点讨论的是I/O多路转接

非阻塞IO

fcntl

一个文件描述符,默认都是阻塞IO。
函数原型如下:

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );

fcntl的cmd的值不同,后面追加的参数也不相同。
fcntl函数有5种功能:

  • 复制一个现有的描述符(cmd=F_DUPFD)。
  • 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD)。
  • 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL)。
  • 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)。
  • 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW)。

我们此处只是用第三种功能,获取/设置文件状态标记,就可以将一个文件描述符设置为非阻塞。

实现函数SetNoBlock

基于fcntl,我们实现一个SetNoBlock函数,将文件描述符设置为非阻塞。

void SetNoBlock(int fd) {int fl = fcntl(fd, F_GETFL);if (fl < 0) {perror("fcntl");return;}fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}
  • 使用F_GETFL将当前的文件描述符的属性取出来(这是一个位图)。
  • 然后再使用F_SETFL将文件描述符设置回去。设置回去的同时,加上一个O_NONBLOCK参数。

轮询方式读取标准输入

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>void SetNoBlock(int fd) {int fl = fcntl(fd, F_GETFL);if (fl < 0) {perror("fcntl");return;}fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}int main() {SetNoBlock(0);while (1) {char buf[1024] = {0};ssize_t read_size = read(0, buf, sizeof(buf) - 1);if (read_size < 0) {perror("read");sleep(1);continue;}printf("input:%s\n", buf);}return 0;
}

相关文章:

  • 【MYSQL】笔记
  • Go 后端中双 token 的实现模板
  • 几种基于比较的排序
  • 建一个结合双向长短期记忆网络(BiLSTM)和条件随机场(CRF)的模型
  • 【java多线程】线程间通信-利用wait和notify轮流按序打印奇数和偶数
  • 什么是着色器 Shader
  • 正则表达式与文本处理的艺术
  • WPS多级标题编号以及样式控制
  • k6学习k6学习k6学习k6学习k6学习k6学习
  • 【Android】从Choreographer到UI渲染(二)
  • Linux虚拟文件系统(1)
  • Spark,数据提取和保存
  • 数组随机重排与维度转换算法
  • 深入解析Python中的Vector2d类:从基础实现到特殊方法的应用
  • ngx_http_random_index_module 模块概述
  • LoadBarWorks:一款赛博风加载动画生成器的构建旅程
  • linux下的 xargs命令使用详解
  • 墨水屏显示模拟器程序解读
  • jqGrid冻结列错行问题,将冻结表格(悬浮表格)与 正常表格进行高度同步
  • HarmonyOS AVPlayer 音频播放器
  • 国家统计局:下阶段要继续发挥宏观政策作用,促进价格合理回升
  • 台湾关闭最后的核电,岛内担忧“非核家园”缺电、涨电价困局难解
  • 英国警方再逮捕一名涉嫌参与首相住宅纵火案嫌疑人
  • 多少Moreless:向世界展示现代中式家具的生活美学
  • 雅典卫城上空现“巨鞋”形状无人机群,希腊下令彻查
  • 流失79载,国宝文物“子弹库帛书”(二、三卷)回归祖国