[Linux]:高级IO

1. IO 理解

1.1 IO 的基本概念

I/O即输入/输出(input/output),是计算机系统中极为关键的操作环节。

在经典的冯诺依曼体系结构框架下,其核心在于数据的传输流向界定了输入与输出的概念。具体而言,当把数据从诸如键盘、鼠标等输入设备拷贝传输到计算机的内存当中,这一过程便被称作输入操作;而当数据从内存拷贝传输至像显示器、打印机这类输出设备时,就被定义为输出操作。

画板

从实际应用场景来看,对文件展开的读写操作实质上就是一种I/O体现。在这种情况下,与之对应的外部设备是磁盘,即读取磁盘上存储的文件内容至内存可视为输入操作,而将内存中的数据写入磁盘形成文件则属于输出操作。同样地,针对网络进行的读写操作同样归属于I/O范畴,这里与之对应的外设是网卡,例如从网络接收数据并存储到内存就是网络输入操作,将内存中的数据通过网卡发送到网络则是网络输出操作。

总的来说,I/O涵盖了计算机与外部设备之间数据交互的各类情形,是实现计算机系统与外界进行信息沟通、数据传递的重要途径,它使得计算机能够获取外部信息并将处理结果反馈出去,从而完成各种复杂的任务和功能实现。

1.2 OS 得知数据就像的方式

操作系统(OS)得知外设当中有数据可读取主要是通过中断机制来实现的。

首先,输入操作是指将数据从外设拷贝到内存的过程,但外设并非随时都有可供操作系统读取的数据。例如在网络访问场景中,用户发出请求报文后需等待从网卡读取服务器响应数据,而在此期间可能存在多种情况致使数据未就绪。

然后,操作系统不会主动频繁检测外设数据是否就绪,因为多数情况下外设无数据,主动检测效率低下。

实际情况是,当外设上有数据就绪时,该外设有权直接将控制信号以中断的形式发送给CPU中的中断控制器。中断控制器会依据中断信号的优先级顺序将其传送给CPU。

而在系统中存有中断向量表,它存储着中断信号与中断处理程序的映射关系。当CPU收到中断信号后,会自动暂停正在运行的程序,依据中断向量表找到并执行该中断信号对应的中断处理程序,待处理完毕后再返回原被暂停的程序继续运行,以此实现得知外设中有数据可读取并进行相应处理的流程。

理解上来说,这种基于中断的机制使得操作系统无需持续关注外设状态,避免了无效检测带来的资源浪费,同时又能及时响应外设的数据就绪情况,保证了数据读取的及时性和系统运行的高效性,是一种合理且高效的外设数据读取感知方式。

1.3 OS 处理网络数据

那么操作系统具体是如何处理网络数据的呢?当操作系统从网卡读取到数据包时,会创建 sk_buff 结构。它的 data 指针指向所读取的数据包。这些 sk_buff 结构以双链表形式组织,操作系统通过对双链表的操作来管理众多数据包。为保证高效的网络报文处理,sk_buff 结构本身需设计得高效。由于要被内核各协议共用,sk_buff 结构需兼容所有网络协议,这使其结构较为复杂。

为了方便理解,下面是一个简单的 sk_buff结构:

struct sk_buff {
    char* transport_header;
    char* network_header;
    char* mac_header;
    char* data;

    struct sk_buff* next;
    struct sk_buff* prev;
};

当操作系统从网卡读取一个数据包时,会依次将数据交给链路层、网络层、传输层、应用层进行解包和分用,最终将数据包中的数据交给了上层用户,而数据包解包与分用向上交付的过程,本质不过是 sk_buff中指针之间的交换。具体过程如下:

  1. 链路层处理:让 sk_buff 结构中的 mac_header 指针指向数据包,通过读取指针指向位置之后的数据获取链路层报头,剩余部分则是网络层要处理的有效载荷,完成链路层解包。
  2. 网络层处理:链路层将有效载荷“传递”给网络层,实际操作是让 sk_buff 结构中的 network_header 指针指向链路层报头之后的数据,然后读取网络层报头,完成网络层解包。
  3. 传输层处理:使 sk_buff 结构中的 transport_header 指针指向网络层报头之后的数据,再读取传输层报头,完成传输层解包。
  4. 数据交付给用户传输层解包后,依据所使用的传输层协议(如TCP或UDP),将剩余数据拷贝到相应的接收缓冲区,供用户读取。

发送数据时,依次在数据前拷贝对应的报头(链路层、网络层、传输层报头),最后根据协议将数据发送(UDP)或拷贝到发送缓冲区(TCP)。整个封装和解包过程中,数据的存储位置基本不变,主要是通过不同指针操作来实现处理。

2. 五种 IO 模型

五种 I/O 模型是在计算机网络编程等领域用于描述操作系统处理输入 / 输出(I/O)操作的不同方式,以下我将为你详细介绍:

2.1 阻塞 IO

阻塞IO(Blocking I/O)是一种在进行输入输出操作时,进程(或线程)会被阻塞的IO模型。

当进程发起一个IO操作时,阻塞 IO 的流程如下:

  • 如果所请求的数据尚未准备好(例如从网络读取数据时,数据还未到达网卡缓冲区;或者从文件读取数据时,文件内容还未加载到内存合适位置等情况),那么发起IO操作的进程就会进入阻塞状态。
  • 在阻塞状态下,进程会暂停执行后续代码,一直等待,直到所请求的IO操作完成,也就是数据准备好并且成功被读取到进程所指定的缓冲区(对于读取操作而言),或者数据成功从进程缓冲区发送出去(对于发送操作而言)。

例如,在一个简单的网络客户端程序中,如果使用阻塞IO从网络套接字读取服务器的响应数据,当客户端发出读取请求后,若服务器响应数据未到达,客户端程序就会阻塞在读取操作处,直到接收到完整数据才会继续执行后续流程。

2.2 非阻塞 IO

非阻塞IO(Non-Blocking I/O)是与阻塞IO相对的一种IO模型,在进行输入输出操作时,进程(或线程)不会因IO操作未完成而一直处于阻塞状态。

当进程发起一个IO操作时,非阻塞 IO 的流程如下:

  • 如果所请求的数据尚未准备好(比如从网络读取数据时,数据还未到达网卡缓冲区;或者从文件读取数据时,文件内容还未加载到内存合适位置等情况),发起IO操作的进程不会进入阻塞状态,而是会立即得到一个反馈结果,告知当前IO操作暂时无法完成,数据还未就绪。
  • 进程在得到数据未就绪的反馈后,可以继续执行后续的其他代码逻辑,而不是像阻塞IO那样一直等待。之后进程可以选择过一段时间再次发起相同的IO操作来检查数据是否已经准备好,直到成功完成IO操作(对于读取操作而言是将数据读取到进程所指定的缓冲区,对于发送操作而言是将数据从进程缓冲区发送出去)。

例如,在一个网络客户端程序中采用非阻塞IO从网络套接字读取服务器的响应数据,当客户端发出读取请求后,如果服务器响应数据未到达,客户端程序不会阻塞在读取操作处,而是可以继续去做诸如更新界面显示、处理其他用户输入等操作,并且会定期或根据一定策略再次发起读取请求来查看数据是否已经到达并可读取。

2.3 信号驱动 IO

信号驱动IO(Signal-Driven I/O)是另一种IO模型,与阻塞IO和非阻塞IO有所不同,它通过信号机制来处理输入输出操作。

当进程发起一个IO操作时:

  • 首先,进程会通过系统调用向内核注册一个信号处理函数,用于在IO操作完成时接收通知信号。这个注册过程告知内核当指定的IO操作就绪(比如数据准备好可读取,或者数据可从缓冲区成功发送出去等情况)时,要发送一个特定的信号给该进程。
  • 然后,进程可以继续执行后续的其他代码逻辑,而不会像阻塞IO那样因为等待IO操作完成而阻塞在此处。在注册完信号处理函数后,进程就去忙自己的其他事情了,不会不断地去轮询检查数据是否就绪,就如同非阻塞IO那样的操作在此模型下是不需要的。
  • 当所请求的IO操作最终完成时,内核会根据之前的注册,发送相应的信号给该进程。进程在接收到这个信号后,就知道对应的IO操作已经就绪了,于是会暂停当前正在执行的代码,转而执行之前注册好的信号处理函数来处理IO操作的后续事宜(比如将数据从内核缓冲区读取到进程指定的缓冲区,或者将进程缓冲区中的数据发送出去等)。

例如,在一个网络服务器程序中采用信号驱动IO来处理客户端的连接请求和数据传输。程序先向内核注册好针对连接请求和数据读取、发送等IO操作的信号处理函数。当有客户端发起连接请求或者有数据要传输时,内核会在相应操作完成后发送信号给服务器程序。服务器程序接收到信号后,就会执行对应的信号处理函数来处理这些IO相关的事宜,比如接受客户端连接、读取客户端发送的数据或者向客户端发送响应数据等,而在等待这些IO操作完成的过程中,服务器程序可以继续处理其他客户端的请求或者进行一些内部的数据维护等工作。

2.4 IO 多路转接

IO多路转接是一种高效的IO处理方式,与其他IO模型有所区别。

当进程使用IO多路转接时,流程如下:

  • 进程先通过系统调用(如 selectpollepoll)将多个IO事件对应的文件描述符交给内核进行监视。这些IO事件可以是多个套接字的读操作、写操作等。
  • 内核会同时监控这些文件描述符对应的IO事件。此时,进程不会因为等待某一个特定IO事件完成而被阻塞,它可以去执行其他的任务。
  • 当这些被监视的文件描述符中有一个或多个IO事件就绪(例如某个套接字有数据可读,或者可以写入数据),进程就可以会对就绪的IO事件进行处理,例如读取数据或者写入数据。

例如,在一个网络服务器程序中使用IO多路转接处理客户端请求。服务器将所有客户端连接对应的套接字文件描述符交给内核监视。当有新的客户端连接请求到来或者已有客户端发送数据时。服务器进程就可以根据具体情况,处理新连接或者读取/发送客户端数据,并且在等待数据就绪的过程中,服务器进程还可以处理其他事务,如记录日志、更新服务器状态等。

2.5 异步 IO

异步IO是一种高效的IO模型,与其他模型有明显差异。

当进程使用异步IO时,流程如下:

  • 进程发起一个IO操作后,不会等待这个IO操作完成,也不会等待数据是否就绪,而是可以直接继续执行后续的其他代码逻辑。
  • 当IO操作完成时(如数据已读取到指定位置或者数据已成功发送),系统会通过某种方式(比如回调函数、信号等)通知进程。进程收到通知后,再对已经完成的IO操作结果进行处理。

例如,在一个网络应用程序中采用异步IO来获取服务器的数据。当程序发起数据读取请求后,它会立刻去执行如检查更新、预加载其他资源等操作。一旦服务器的数据读取完成,系统会通过事先设定的回调函数通知程序,程序就可以对读取到的数据进行后续处理,如展示数据、分析数据等。

3. 同步通信与异步通信

3.1 同步通信

  • ****在同步通信的情境下,当发起一个调用操作时,调用者会一直处于等待状态,直至获取到该调用的最终结果才会继续后续的流程。也就是说,这个调用不会在未得到结果前就返回,只有当结果明确返回给调用者时,整个调用过程才算完成。

比如你在网上购买一件商品,点击“提交订单”按钮后(发起调用),页面会一直显示加载状态,直到系统处理完订单相关的所有流程,如库存检查、价格核算、支付确认等,并返回一个明确的提示,如“订单提交成功”或者告知你失败的原因(这就是得到返回值),你才能进行后续操作,比如继续浏览其他商品或者查看订单详情等。在这个过程中,你作为调用者(点击提交订单的操作主体)是主动在等待订单提交这个调用的结果。

3.2 异步通信

与同步通信截然不同,异步通信在调用发出之后,调用本身会立即返回,并不会马上给出结果。调用者在发出调用后可以继续去执行其他的任务或操作,而不需要一直等待结果。被调用者会在合适的时候,通过诸如更新状态让调用者可查看、发送通知消息或者执行回调函数等方式,来告知调用者关于该调用的最终结果。

还是以网上购物为例,当你点击“提交订单”按钮(发起调用)后,页面可能只是短暂显示一个提示,如“订单正在处理中”,然后你就可以继续浏览其他商品、查看购物车等其他操作了(因为调用已经返回,你可以继续做其他事)。之后,系统会通过发送短信通知你订单是否提交成功(这就是通过通知告知调用者结果),或者在订单页面更新订单状态供你查看(通过状态告知结果),又或者在后台执行一个回调函数来完成一些后续相关的处理(比如根据订单结果为你推荐相关商品等),而这些告知结果的操作都是在你已经继续进行其他活动之后发生的。

其中需要注意的是多进程/线程里的同步与互斥,和同步通信概念不同。进程/线程同步是保证数据安全下按序访问临界资源,避免饥饿。而同步IO则关乎进程/线程与操作系统关系,涉及是否主动参与IO过程。

4. 其他高级IO

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

5. fcntl 函数

我们在打开文件 open 时,默认为阻塞打开,但是用open函数携带O_NONBLOCKO_NDELAY选项就可以可用非阻塞的方式打开文件。

但是如果我们要将已打开文件或套接字设为非阻塞状态,需用用到fcntl函数,其用法如下:

  1. 函数原型:int fcntl(int fd, int cmd,… /* arg */);
  2. 参数说明:
  • fd:已打开的文件描述符。
  • cmd:要进行的操作,不同cmd取值对应不同功能,常见取值及功能如下:
    • F_DUPFD:复制一个现有的描述符。
    • F_GETFDF_SETFD:获得/设置文件描述符标记。
    • F_GETFLF_SETFL:获得/设置文件状态标记。
    • F_GETOWNF_SETOWN:获得/设置异步I/O所有权。
    • F_GETLKF_SETLKF_SETLKW:获得/设置记录锁。
  1. 返回值:函数调用成功,返回值取决于具体操作。函数调用失败,返回 -1,同时错误码会被设置。

然后通过以下代码可将指定文件描述符设为非阻塞状态:

bool SetNonBlock(int fd)
{
    int fl = fcntl(fd, F_GETFL);
    if (fl < 0){
        std::cerr << "fcntl error" << std::endl;
        return false;
    }
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
    return true;
}

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

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

相关文章

数据结构之二叉树--前序,中序,后序详解(含源码)

二叉树 二叉树不能轻易用断言&#xff0c;因为树一定有空 二叉树链式结构的实现 在学习二叉树的基本操作前&#xff0c;需先要创建一棵二叉树&#xff0c;然后才能学习其相关的基本操作。 typedef int BTDataType; typedef struct BinaryTreeNode {BTDataType _data;struct B…

Java-I/O框架13:文件夹的递归遍历和递归删除

视频链接&#xff1a;16.29 递归遍历和递归删除_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Tz4y1X7H7?spm_id_from333.788.videopod.episodes&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5&p29 1.文件夹的递归遍历 public class ListDirectoryDemo01 {pub…

Qt 无法获取调试输出

问题 使用Qt进行编程时&#xff0c;发现在应用程序输出窗口无法输出调试信息&#xff0c;在源代码里的debug输出信息一个也不显示。 如下图&#xff1a; 解决方案 同一个IDE开启多次&#xff0c;会导致出现这样的问题&#xff0c;可以把QtCreator关闭只留一个。

影响神经网络速度的因素- FLOPs、MAC、并行度以及计算平台

影响神经网络速度的四个主要因素分别是 FLOPs&#xff08;浮点操作数&#xff09;、MAC&#xff08;内存访问成本&#xff09;、并行度以及计算平台。这些因素共同作用&#xff0c;直接影响到神经网络的计算速度和资源需求。 1. FLOPs&#xff08;Floating Point Operations&a…

02_ElementUI

一.前端工程化 1.1 概述 前端工程化是使用软件工程的方法来单独解决前端的开发流程 中模块化、组件化、规范化、自动化的问题,其主要目的为了 提高效率和降低成本。 1.2 NodeJS的安装 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环 境&#xff0c;可以使 JavaS…

Pytorch实现运动鞋识别

Pytorch实现运动鞋识别 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 电脑系统&#xff1a;Windows11 显卡型号&#xff1a;NVIDIA Quadro P620 语言环境&#xff1a;python 3.9.7 编译器&#xff1a;j…

[卷积神经网络]使用YOLOv11训练自己的模型

YoloV11的源码&#xff1a;YOLOv11 一、数据集准备 首先&#xff0c;准备好自己的数据集&#xff0c;包含图像文件和标注文件&#xff0c;因为我的数据集上Voc格式&#xff0c;所以需要先转为yolo格式&#xff0c;可以使用下面的脚本进行转换。 import os import shutil impo…

vue+exceljs前端下载、导出xlsx文件

首先安装插件 npm install exceljs file-saver第一种 简单导出 //页面引入 import ExcelJS from exceljs; import {saveAs} from file-saver; export default {methods: { /** 导出操作 */async handleExportFun() {let that this// 获取当前年月日 用户下载xlsx的文件名称设…

pytest自动化测试框架详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 Pytest是一种基于Python编程语言的自动化测试框架&#xff0c;它提供了丰富的功能和灵活的扩展性&#xff0c;可以用于单元测试、集成测试、功能测试、端到端测试…

解决com.mysql.jdbc.NonRegisteringDriver内存泄漏问题

1. 问题背景 线上出现内存报警&#xff0c;通过dump文件&#xff0c;MAT分析&#xff0c;发现mysql-connector-java 有内存泄漏问题 2.问题分析 然后看大对象列表&#xff0c;NonRegisteringDriver 对象确实占内存比较多&#xff0c;里面村的数据库连接的虚引用占比较多 3.解…

Golang | Leetcode Golang题解之第547题身份数量

题目&#xff1a; 题解&#xff1a; func findCircleNum(isConnected [][]int) (ans int) {n : len(isConnected)parent : make([]int, n)for i : range parent {parent[i] i}var find func(int) intfind func(x int) int {if parent[x] ! x {parent[x] find(parent[x])}re…

CSS实现文字渐变效果

效果图&#xff1a; 代码&#xff1a; h1 {font-size: 100px;color:linear-gradient(gold,deeppink);background-image:linear-gradient( -gold, deeppink); /*春意盎然*///背景被裁剪成文字的前景色。background-clip:text;/*兼容内核版本较低的浏览器*/-webkit-background-c…

24/11/8 算法笔记 t-SNE降维算法

t-SNE算法的核心实现涉及几个关键步骤&#xff0c;主要包括概率分布的构建、梯度计算和优化。以下是这些步骤的简要说明&#xff1a; 1. **概率分布的构建**&#xff1a; - 在高维空间中&#xff0c;t-SNE使用高斯分布&#xff08;Gaussian distribution&#xff09;来构建…

企业微信-消息推送之微信客服-接收消息和事件

一&#xff1a;企微实现和企业间的微信客服消息接收和事件原理 新版企微主要通过2个阶段实&#xff0c;第一个&#xff1a;消息推送 概述 - 文档 - 企业微信开发者中心 &#xff0c;第二个&#xff1a;微信客服 接收消息和事件 - 文档 - 企业微信开发者中心 二&#xff1a;代码…

Ascend Extension for PyTorch是个what?

1 Ascend Extension for PyTorch Ascend Extension for PyTorch 插件是基于昇腾的深度学习适配框架&#xff0c;使昇腾NPU可以支持PyTorch框架&#xff0c;为PyTorch框架的使用者提供昇腾AI处理器的超强算力。 项目源码地址请参见Ascend/Pytorch。 昇腾为基于昇腾处理器和软…

【React】React 生命周期完全指南

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 React 生命周期完全指南一、生命周期概述二、生命周期的三个阶段2.1 挂载阶段&a…

开源模型应用落地-glm模型小试-glm-4-9b-chat-压力测试(六)

一、前言 GLM-4是智谱AI团队于2024年1月16日发布的基座大模型&#xff0c;旨在自动理解和规划用户的复杂指令&#xff0c;并能调用网页浏览器。其功能包括数据分析、图表创建、PPT生成等&#xff0c;支持128K的上下文窗口&#xff0c;使其在长文本处理和精度召回方面表现优异&a…

程序开发时单数复数及前缀的命名规范(目录名、文件名、函数名、变量名、数据库字段等)

在程序开发中&#xff0c;我总是被单复数搞得头疼&#xff0c;以前采用了最舒服的方法&#xff0c;一刀切&#xff1a;全部单数&#xff0c;因为理由也很简单&#xff0c;单数都可以作为定语解释&#xff0c;比如/util&#xff0c;可以认为真正的名称是/util files或者/util di…

Spring Boot原理全解析:如何让开发更轻松高效(二)-起步依赖、自动装配

通过这篇博客&#xff0c;读者将能够掌握 Spring Boot 中的配置优先级和 Bean 管理的核心原理&#xff0c;为开发更加高效、可维护的 Spring Boot 应用打下坚实的基础。 目录 前言 起步依赖 自动配置 概述 常见方案 概述 方案一 方案二 总结 前言 通过这篇博客&#xf…

力扣动态规划基础版(矩阵型)

62.不同路径&#xff08;唯一路径问题&#xff09; 62. 不同路径https://leetcode.cn/problems/unique-paths/ 方法一&#xff1a;动态规划 找状态转移方程&#xff0c;也就是说它从左上角走到右下角&#xff0c;只能往右或者往下走&#xff0c;那么设置一个位置为&#xff…