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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用Flutter开发执行操作系统shell命令的工具

简介 Flutter是一种由Google开发的移动应用程序开发框架&#xff0c;它允许开发人员使用单个代码库构建高性能、高质量的移动体验。而Android终端命令行工具则允许用户在Android手机上运行类似于Linux的操作系统命令。本文的目的是介绍如何在Flutter应用中开发一个Android终端命…

CUDA学习笔记(二)CUDA简介

本篇博文转载于https://www.cnblogs.com/1024incn/tag/CUDA/&#xff0c;仅用于学习。 CUDA是并行计算的平台和类C编程模型&#xff0c;我们能很容易的实现并行算法&#xff0c;就像写C代码一样。只要配备的NVIDIA GPU&#xff0c;就可以在许多设备上运行你的并行程序&#xf…

什么是马尔科夫随机场?

马尔科夫随机场&#xff0c;也称为马尔可夫网&#xff08;Markov Network&#xff09;&#xff0c;是一种概率图模型&#xff0c;用于表示随机变量之间的依赖关系。它是由若干个随机变量组成的无向图&#xff0c;其中节点代表随机变量&#xff0c;边代表它们之间的相互作用或依…

Node.js的crypto模块 加密

Node.js的crypto模块提供了许多加密和解密功能&#xff0c;包括对称加密、非对称加密、哈希函数等。在本篇文章中&#xff0c;我们将详细介绍Node.js的crypto模块的API、代码注释和举例。 加密和解密 对称加密 对称加密算法使用相同的密钥进行加密和解密&#xff0c;例如AES…

Android 13.0 进入recovery模式(等待用户选择recovery模式界面)进入自动恢复出厂设置模式

1.概述 在13.0的系统产品开发中,由于产品硬件有按钮,按钮执行恢复出厂设置功能,需要实现自动恢复出厂设置的功能,这就需要去掉等待输入recovery模式的相关代码,改成默认恢复出厂模式就实现这个功能了 2.进入recovery模式(等待用户选择recovery模式界面)进入自动恢复出厂设…

浅谈uniapp中开发安卓原生插件

其实官方文档介绍的比较清楚而且详细,但是有时候他太墨迹,你一下子找不到自己想要的,所以我总结了一下开发的提纲,也是为了自己方便下次使用。 1.第一步,下载官方提供的Android的示例工程,然后倒入UniPlugin-Hello-AS工程请在App离线SDK中查找,之后Android studio,编译运行项目…

自编efi文件测试vmware虚拟机如何进入UEFI环境

同事突然让帮忙编一下UEFI&#xff0c;之前完全没有接触过&#xff0c;在此粗鲁记录其过程。 UEFI的开源框架是edk2&#xff0c;开发环境配置起来还是有些麻烦&#xff0c;完全按照文档编译不过&#xff0c;经人帮助总算编译通过&#xff0c;但如何测试又是问题&#xff1b;网…

【T+】畅捷通T+增加会计科目提示执行超时已过期。

【问题描述】 在畅捷通T软件中&#xff0c; 增加会计科目的时候提示&#xff1a; 通过DataTable插入ext扩展表出错:执行超时已过期。 完成操作之前已超时或服务器未响应。 操作已被用户取消。 语句已终止。 【解决方法】 【方法一】 注销用户登录&#xff0c;回到软件登录界面…

FFmpeg和rtsp服务器搭建视频直播流服务

下面使用的是ubuntu的&#xff0c;window系统可以参考&#xff1a; 通过rtsp-simple-server和ffmpeg实现录屏并发布视频直播_rtsp simple server_病毒宇宇的博客-CSDN博客 一、安装rtsp-simple-server &#xff08;1&#xff09;下载rtsp-simple-server 下载地址&#xff1a;R…

1024啊啊啊啊啊啊

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

Kotlin中的Lambda表达式基本定义和使用

在Kotlin中&#xff0c;Lambda表达式是一种简洁的方式来定义匿名函数。Lambda表达式可以作为函数的实际参数或者返回值&#xff0c;使得函数成为高阶函数。本篇博客将介绍Lambda表达式的基本概念以及使用方法&#xff0c;并提供相关的示例代码。 Lambda表达式的基本概念 Lamb…

vsCode 格式化配置

学习目标&#xff1a; 基于 vsCode 配置格式化工具&#xff0c;提高&#xff08;React、Vue &#xff09;开发效率  1. vsCode 安装 prettier 插件并启用  2. 修改配置文件 setting.json setting.json 位置&#xff1a; 依次点击 替换内容&#xff1a;↓ {"git.enab…

智加科技与东风柳汽达成深度合作 自动驾驶重卡计划2024年初量产交付

&#xff08;2023年10月19日&#xff0c;苏州&#xff09;全球领先的重卡自动驾驶技术公司智加科技与东风柳汽宣布&#xff0c;双方共同开发的自动驾驶重卡H7计划2024年初实现量产交付。未来&#xff0c;双方将携手推出安全可靠、高性价比、性能卓越的自动驾驶重卡产品&#xf…

什么年代了,还在用FastQC?试试Falco吧

什么年代了&#xff0c;还在用FastQC&#xff1f;试试Falco吧 目前大部分的教程在质控上都是推荐的FastQC&#xff0c;然而它有一个不足&#xff0c;就是虽然名字上有一个Fast&#xff0c;但是它还不够Fast&#xff0c;真正的快&#xff0c;还得是Falco。 如何安装&#xff1…

STM32 HAL高级定时器正交编码模式案例

STM32 HAL高级定时器正交编码模式案例 &#x1f516;基于stm32F030RBT6单片机采用高级定时器1&#xff0c;编码器模式&#xff0c;测试EC11编码器。 &#x1f3ac;EC11测试效果&#xff1a; &#x1f33f;STM32定时器编码器有3种映射模式: ✨本次采用的是上面的模式3&#x…

postgresql14-模式的管理(三)

基本概念 postgresql成为数据库管理系统DBMS&#xff0c;在内存中以进程的形态运行起来成为一个实例&#xff0c;可管理多个database。 数据库databases&#xff1a;包含表、索引、视图、存储过程&#xff1b; 模式schema&#xff1a;多个对象组成一个模式&#xff0c;多个模…

2023年10月22日找工作面试交流遇到的基本问题

交叉编译解决的痛点问题 不同硬件体系结构之间的编译问题。嵌入式系统开发需要在主机上编写代码。提高效率和节省时间。软件移植和管理依赖关系。 不同硬件体系结构之间的编译问题&#xff1a;例如&#xff0c;你开发了一个针对Intel x86架构的应用程序&#xff0c;但想要在Ra…

学成在线第一天-课程内容管理服务搭建以及查询课程接口设计

目录 一、搭建课程内容管理服务 二、设计接口 三、面试题 四、总结 一、搭建课程内容管理服务 没什么好说的&#xff0c;直接就是创建内容模块 然后这个继承父模块&#xff0c;然后再课程内容模块下面创建三个子模块&#xff0c;model、sevice、controller model依赖base…

分享一下微信小程序的文章中怎么添加营销活动

在数字化时代&#xff0c;小程序已经成为企业营销的重要工具。通过小程序&#xff0c;企业可以提供更加便捷、高效的服务&#xff0c;吸引更多的用户和客户。本文将以小程序营销活动为主题&#xff0c;介绍如何在小程序文章中加入营销活动&#xff0c;提高品牌知名度和销售额。…