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

I/O模型之非阻塞IO

简介

五种IO模型  

        阻塞IO  

        非阻塞IO  

        信号驱动IO  

        IO多路转接   

        异步IO

代码书写

        非阻塞IO

 

再次理解IO

什么是IO?什么是高效的IO?

为了理解后面的一个问题,我们首先要再重新理解一下什么是IO

在之前的网络介绍中,我们其实已经知道了IO的本质其实就是拷贝

        通过前面的 网络 过程中,我们所做的一切都是在把数据在拷贝来拷贝去,但是等这个部分就是由操作系统来控制的,因为把数据发送出去的前提是把数据从外设的磁盘中先把数据拷贝到发送缓冲区之中,通常IO的大部分时间的占比都是 "等" 这个行为造成的,拷贝数据其实快不了多少,拷贝速度只能依靠设备自身的配置,所以为了实现高效IO,我们只能 "等" ,这方面入手

        所谓的高效,就是把 "等" 的时间占比降低,只要减少 "等" 就是在实现高效,拷贝数据只能从硬件方面入手,换设备之类的啊,硬盘用SSD的啊,这类不是我们需要考虑的,我们这里只考虑 "等"

五种IO模型

我们用钓鱼的例子理解

故事背景

分析

解释

        其中阻塞式IO、非阻塞式IO、信号驱动式IO、多路转接/多路复用、异步IP统称为IO的五种模型,现在的全部的IO脱离不开这五种模型

        需要注意的是,我们通常大部分使用的其实还是阻塞式IO,因为简单,但是高效就是指的多路转接/多路复用

差别

阻塞IO

阻塞IO是最常见的IO模型

阻塞IO: 在内核将数据准备好之前, 系统调用会一直等待. 所有的套接字, 默认都是阻塞方式.

非阻塞IO

        非阻塞IO: 如果内核还未将数据准备好, 系统调用仍然会直接返回, 并且返回EWOULDBLOCK错误码

        非阻塞IO往往需要程序员循环的方式反复尝试读写文件描述符, 这个过程称为轮询. 这对CPU来说是较大的浪费, 一般只有特定场景下才使用.

信号驱动IO

信号驱动IO: 内核将数据准备好的时候, 使用SIGIO信号通知应用程序进行IO操作

IO多路转接

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


 

        select只负责等这个行为,并且可以等多个文件描述符,是操作系统专门准备的一个接口,recvfrom只负责拷贝这个行为,也是操作系统专门准备的接口,因为这两个接口是解耦的,所以当select准备好了话,那么recvfrom就一定有数据可以拷贝,一定可以拷贝成功

        并且进程\线程的消耗是比较大的,但是多路转接就不会创建多个进程\线程,于是它的成本也是比较低的

异步IO

        异步IO: 由内核在数据拷贝完成时, 通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据)

        操作系统自己等待数据,进程只是发起者,操作系统把数据都放在一个缓冲区之中,当满的时候,就直接执行进程传进来的方法就行了,进程本身不参与IO等待任何一个行为

小结

        任何IO过程中, 都包含两个步骤. 第一是等待, 第二是拷贝. 而且在实际的应用场景中, 等待消耗的时间往往都远远高于拷贝的时间. 让IO更高效, 最核心的办法就是让等待的时间尽量少.

非阻塞IO

在代码接口中,我们可以通过传递参数来使用哪一种IO读取方式

不过我们通常是有一个函数可以专门来解决这方面的问题的

函数:fcntl

使用前先获取文件操作符

非阻塞选项

书写非阻塞转化函数

注意头文件的引入

阻塞式IO

如下图,0号文件描述符就是stdin,这里直接就是代表了键盘的输入了

设置为非阻塞

直接把之前写的函数用上

这里因为0号是键盘,而这里的1号就是屏幕,因为非阻塞所以键盘的输入是不会影响屏幕的打印的,这里的提示符一直在不停的循环打印(这里是sleep一秒的结果),0号当没有数据来的时候就会立刻返回,这样就可以做其他的事情了

我们可以进一步封装,当没有数据来的时候,可以让它做其他的事情

在循环内不断调用这个函数

存放的函数,用来演示作用

返回值怎么区别错误

因为非阻塞,当它没有数据的时候会立即返回,当你打印出来这个值的时候,会发现是-1和错误信息返回用的同一个值,那么我们如何区别真的错误还是因为没有数据导致的返回呢?

我们再一次查看read接口的返回值就可以知道,当被返回时还会有一个动作,即,错误码被立即设置

这时候我们可以直接通过打印出错误码的形式看到是因为什么原因导致的返回,其次我们知道错误码本质上是一个个的宏,利用这些宏,我们就可以实现区分这些错误信息了

结果显示,这里给的错误信息是资源未就绪,这样我们就可以知道是什么原因了

其实在read返回值里面专门提供了这么一个宏来标识,这类的信息

可以看出来其实就是 11

为此我们这样修改代码,将空闲时候执行其他函数的行为放到,错误码被置为-1并且本身不是错误的时候执行

这种错误其实是系统调用被中断了,即当正在读取的时候,一个信号过来,直接把读取中断了,这种错误也不算是失败错误,所以再做一个判断

至此非阻塞就不再深入了

源码

makefile
testNonBlock:main.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f testNonBlock

main.cc
#include "util.hpp"
#include <cstdio>
#include <vector>
#include <functional>using func_t = std::function<void()>;// 这是一个宏函数,用来把函数加载到数组当中去的
#define INIT(v)                  \do                           \{                            \v.push_back(pringLog);   \v.push_back(download);   \v.push_back(executeSql); \} while (0)// 这是一个宏函数,用来运行函数数组里面存放的函数的
#define EXEC_OTHER(cbs)            \do                             \{                              \for (auto const &cb : cbs) \cb();                  \} while (0)int main()
{std::vector<func_t> cbs;INIT(cbs); // 调用宏函数,运行存放在函数数组中的函数的setNonBlock(0);char buffer[1024];while (true){printf(">>> "); // 提示符fflush(stdout);ssize_t s = read(0, buffer, sizeof(buffer) - 1);if (s > 0){buffer[s - 1] = 0;std::cout << "echo# " << std::endl;}else if (s == 0){std::cout << "read end" << std::endl;break;}else{// 1.当不输入的时候,底层没有数据,这算是错误吗? 不算错误,只不过以错误的形式返回了(-1)// 2.那么如何区别,真的错误,还是因为底层没有数据导致的错误返回?// std::cout << "EAGAIN: " << EAGAIN << " EWOULDBLOCK: " << EWOULDBLOCK << std::endl;if (errno == EAGAIN){std::cout << "我没错, 只是没有数据" << std::endl;EXEC_OTHER(cbs); // 宏调用,用来把函数加载到函数数组中去}else if(errno == EINTR){continue;   // 系统信号导致的返回,直接让它继续读取就行了}else{//这类表示真正的错误,在前面已经把其他非错误的错误码处理解决之后,将错误信息打印出来std::cout << "s : " << s << " errno: " << strerror(errno) << std::endl;break;}}sleep(1);}
}

util.hpp
#pragma once#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <cerrno>// 这是一个讲文件描述符转为非阻塞式的函数
void setNonBlock(int fd)
{int f1 = fcntl(fd, F_GETFL);if(f1 < 0){std::cerr << "fcntl : " << strerror(errno) << std::endl;return;}fcntl(fd, F_SETFL, f1 | O_NONBLOCK); // 设置为非阻塞
}// 工具函数
void pringLog()
{std::cout << "this is a log" << std::endl;
}void download()
{std::cout << "this is a download" << std::endl;
}void executeSql()
{std::cout << "this is a executeSql" << std::endl;
}

下期预告:I/O多路转接之select

相关文章:

I/O模型之非阻塞IO

简介 五种IO模型   阻塞IO   非阻塞IO   信号驱动IO   IO多路转接    异步IO 代码书写 非阻塞IO 再次理解IO 什么是IO&#xff1f;什么是高效的IO&#xff1f; 为了理解后面的一个问题&#xff0c;我们首先要再重新理解一下什么是IO 在之前的网络介绍中&#xff…...

2023版 STM32实战11 SPI总线读写W25Q

SPI全称 英文全称&#xff1a;Serial peripheral Interface 串行外设接口 SPI特点 -1- 串行(逐bit传输) -2- 同步(共用时钟线) -3- 全双工(收发可同时进行) -4- 通信只能由主机发起(一主,多从机) 开发使用习惯和理解 -1- CS片选一般配置为软件控制 -2- 片选低电平有效,从…...

Spring Security认证源码解析(示意图)

建议先看完Spring Security总体架构介绍和Spring Security认证架构介绍&#xff0c;然后从FilterChainProxy的doFilterInternal函数开始&#xff0c;配合文章进行debug以理解Spring Security认证源码的执行流程。 在之前的Spring Security认证架构介绍中&#xff0c;我们已经知…...

2023.10.22 关于 定时器(Timer) 详解

目录 引言 标准库定时器使用 自己实现定时器的代码 模拟实现的两大方面 核心思路 重点理解 自己实现的定时器代码最终代码版本 引言 定时器用于在 预定的时间间隔之后 执行特定的任务或操作 实例理解&#xff1a; 在服务器开发中&#xff0c;客户端向服务器发送请求&#…...

【STM32】GPIO控制LED(寄存器版)

在开始之前记得先准备好环境&#xff1a; STM32F103核心板下载教程.pdf 林何/STM32F103C8 - 码云 - 开源中国 (gitee.com) 一、STM32的GPIO模块数据手册详解 每个GPIO端口对应16个引脚&#xff0c;例GPIOA&#xff08;PA0~PA15&#xff09;内核cpu就可以通过APB2总线对寄存器…...

Spring Boot OAuth 2.0整合—高级配置

一、概述 HttpSecurity.oauth2Login() 为定制OAuth 2.0登录提供了大量的配置选项。主要的配置选项被分组到它们的协议端点对应处。 例如&#xff0c;oauth2Login().authorizationEndpoint() 允许配置授权端点&#xff0c;而 oauth2Login().tokenEndpoint() 允许配置令牌端点。…...

软考-虚拟专用网原理与应用

本文为作者学习文章&#xff0c;按作者习惯写成&#xff0c;如有错误或需要追加内容请留言&#xff08;不喜勿喷&#xff09; 本文为追加文章&#xff0c;后期慢慢追加 by 2023年10月 虚拟专用网概念 虚拟专用网&#xff08;Virtual Private Network&#xff09;是一种通过…...

clock_property 时钟的常用属性

get_property [get_clocks] property_option 1. period get_property [get_clocks] period 查询所有clock 的周期&#xff0c;如果存在loops会生成CTE_loops.rpt 2.clock_network_pins 查询clock所有的pins 3.generated_clocks_extended 查询clock分频产生的generate…...

平板有必要买触控笔吗?推荐的ipad手写笔

iPad之所以能吸引这么多人&#xff0c;主要是因为它的功能出色。用来画画、做笔记&#xff0c;也是一种不错的体验。但如果只是用来看电视和打游戏的话&#xff0c;那就真的有点大材小用了。如果你不需要昂贵的苹果电容笔&#xff0c;也不需要用来专业的绘图&#xff0c;那你可…...

Qt扫描-QMoive 理论总结

QMoive 理论总结 一、概述二、使用1. 使用2. 信号发出时机 三、控制的相关槽函数四、信号 一、概述 QMovie类是一个使用QImageReader播放 动画 的方便类。这个类用于显示没有声音的简单动画&#xff0c;一般即是 gif 动画。如果要显示视频和媒体内容&#xff0c;请使用Qt Mult…...

类似东郊到家预约家政保洁小程序搭建

随着生活水平的提高&#xff0c;人们对健康养生的需求越来越重视&#xff0c;按摩作为一种传统的养生方式&#xff0c;备受关注。为了方便用户快速、方便地预约按摩服务&#xff0c;本文将介绍一款按摩预约小程序的开发。 首先&#xff0c;我们通过市场调研和分析发现&#xf…...

[补题记录] Atcoder Beginner Contest 325(E、F)

URL&#xff1a;https://atcoder.jp/contests/abc325 目录 E Problem/题意 Thought/思路 Code/代码 F Problem/题意 Thought/思路 Code/代码 E Problem/题意 有一个二维矩阵&#xff0c;D[i][j] 表示从 i 到 j 的距离。从 i 到 j 有两种方式&#xff1a; 坐汽车&…...

1024啊啊啊啊啊啊

1024 程序员节快乐&#xff0c;没什么想发的&#xff0c;只是想要个1024胸章。...

淘宝商品详情API接口(H5端和APP端),淘宝详情页,商品属性接口,商品信息查询

一、接口参数说明&#xff1a;提取淘宝商品详情页各项数据&#xff0c;包含skuid、价格、收藏数、加购数、月销售量、主图、标题、详情页图片等页面上可以看奥的数据都可以拿到。 点击获取key和secret 二、使用场景 1、商品销售情况分析&#xff0c;根据销量调整活动方案&am…...

JVM的几个面试重点

JVM的内存区域划分 JVM类加载机制 前言 Java程序最开始是一个 .java 的文件&#xff0c;JVM把它编译成 .closs 文件&#xff08;字节码文件&#xff09;&#xff0c;运行 Java 程序&#xff0c; JVM 就会读取 .class 文件&#xff0c;把文件内容读取到内存中&#xff0c;构造出…...

[yolo系列:YOLOV7改进-添加CoordConv,SAConv.]

文章目录 概要CoordConvSAConv 概要 CoordConv&#xff08;Coordinate Convolution&#xff09;和SAConv&#xff08;Spatial Attention Convolution&#xff09;是两种用于神经网络中的特殊卷积操作&#xff0c;用于处理图像数据或其他多维数据。以下是它们的简要介绍&#x…...

【万字实操】可视化运维平台openGauss Datakit,带你轻松玩转openGauss 5.0

openGauss Datakit:openGauss社区推出的可视化的运维工具. 特性优势 初级用户学习openGauss门槛高让你望而却步&#xff1f;openGauss Datakit一键化安装企业版集群、监控、日志分析、SQL诊断&#xff0c;让你快速上手&#xff0c;快速部署&#xff0c;从容面对企业环境&#…...

《动手学深度学习 Pytorch版》 10.1 注意力提示

10.1.1 生物学中的注意力提示 “美国心理学之父” 威廉詹姆斯提出的双组件&#xff08;two-component&#xff09;框架&#xff1a; 非自主性提示&#xff1a;基于环境中物体的突出性和易见性 自主性提示&#xff1a;受到了认知和意识的控制 10.1.2 查询、键和值 注意力机制…...

C# 写入文件比较

数据长度&#xff1a;128188个long BinaryWriter每次写一个long 耗时14.7828ms StreamWriter每次写一个long 耗时44.0934 ms FileStream每次写一个long 耗时20.5142 ms FileStream固定chunk写入&#xff0c;循环操作数组&#xff0c;耗时13.4126 ms byte[] chunk new byte[d…...

医院设备利用(Use of Hospital Facilities, ACM/ICPC World Finals 1991, UVa212)rust解法

医院里有n&#xff08;n≤10&#xff09;个手术室和m&#xff08;m≤30&#xff09;个恢复室。每个病人首先会被分配到一个手术室&#xff0c;手术后会被分配到一个恢复室。从任意手术室到任意恢复室的时间均为t1&#xff0c;准备一个手术室和恢复室的时间分别为t2和t3&#xf…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...

区块链技术概述

区块链技术是一种去中心化、分布式账本技术&#xff0c;通过密码学、共识机制和智能合约等核心组件&#xff0c;实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点&#xff1a;数据存储在网络中的多个节点&#xff08;计算机&#xff09;&#xff0c;而非…...