五种IO模型

目录

一、对IO的重新认识

二、IO的五种模型

1.阻塞IO

2.非阻塞IO

3.信号驱动IO

4.IO多路转接

5.异步IO

6.一些概念的解释

三、非阻塞IO的代码实现

1.fcntl

2.实现工具类

3.实现主程序


一、对IO的重新认识

如果有人问你IO是什么,你该怎么回答呢?

你可能会说,IO不就是input和output表示输入和输出,输入表示把数据从硬盘等外设拷贝到内存,而输出表示把数据从内存拷贝到其他外设。

虽然这样说没什么大问题,但还不够深刻。

我们不妨设想下面的现象,有一个进程,调用read/recv这样的系统调用读取数据。如果此时读取条件不满足,那就没有数据可供进程读取,进程只就会一直等待数据准备好。

IO除了拷贝数据需要消耗时间,还包含这个等待的过程所以我们使用的系统调用除了拷贝代码,也包含了等的这部分代码。

也就是说,IO=等+数据拷贝

那什么是高效IO呢?

我们知道,IO过程我们在意的是拷贝,而不是等待。而拷贝需要的时间是由电路还有系统实现等保证的。随着科技的发展,拷贝本身花费的时间已经基本没有提升空间了,所以拷贝本身的效率已经很难再有提升了。那么等待时间的长度就决定了IO的效率。

换句话说,单位时间内,等待的比重越低,IO效率越高。

二、IO的五种模型

1.阻塞IO

在内核将数据准备好之前,系统调用会一直等待。我们之前写代码使用的IO接口读取文件描述符,默认都使用阻塞IO方式。

下图就是阻塞IO的示意图,进程调用recvfrom这样的IO接口从内核中读取数据。如果数据没有准备好,进程就会阻塞在调用处等待,数据准备好后,才会将内核中的数据拷贝到用户缓冲区,并给出返回值。

阻塞IO是最常见的IO模型,也最简单,我们之前写的所有代码,IO都是阻塞式的。

2.非阻塞IO

如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回EAGAN或者EWOULDBLOCK错误码。

如图所示,进程调用recvfrom从内核缓冲区中读取数据。如果数据没有准备好,就会给进程返回一个EWOULDBLOCK错误码,告诉进程数据还没准备好,进程就会接着去干自己的事情。

过了一段时间,进程还会调用recvfrom读取数据,不断反复,直到数据准备好。接着系统调用完成拷贝并返回成功的返回值。

非阻塞IO需要程序员设计循环代码,反复尝试读写文件描述符,这个过程称为轮询。但轮询对CPU有一定的性能浪费,只有特定场景下才使用。

3.信号驱动IO

信号驱动IO会在内核将数据准备好的时候,发送SIGIO信号通知进程进行IO操作。

如图所示,信号驱动IO模型,该模式使用信号处理函数执行IO。

首先使用signal注册信号处理函数为包含IO系统调用的函数。所以只要进程收到信号,就可以在处理函数中调用recvfrom拷贝已经准备好的数据。

也就是说,只要数据准备完成了,进程就会收到信号,进程直接来拷贝就可以了。其余时间进程还可以继续执行自己的代码。

但是我们之前也说过,如果我们给一个进程同时发很多信号,只有两个能被最终递达。而这里的信号丢失就相当于读取次数减少,就相当于数据丢失。所以,这种很少有符合这种模式的IO状态。

4.IO多路转接

IO多路转接可以理解为多个阻塞IO同时进行,并不断遍历检测哪个IO的文件描述符准备好了,准备好了就会执行拷贝。

如图所示为IO多路转接模式,它将IO的等待和拷贝分开了。

进程调用select系统调用等待内核中的数据就绪,就绪以后会通知进程调用recvfrom来将数据拷贝到用户缓冲区中。

由于多路转接可以同时等待多个文件描述符。所以,当一个或者多个文件的缓冲区中数据就绪时,都会通知上层用户读取。而且每个拷贝过程也是并行的,还是免不了等,但是等的比重降低了很多,从而提高了IO的效率。

多路转接既是效率最高的IO模式,也是我们以后讲解的重点。

5.异步IO

当一个异步IO调用发出后,调用者不会立刻得到结果,而是在调用发出后,被调用者通过状态、信号等来通知调用者,或通过回调函数处理这个调用。

下图表示异步IO,进程调用aio_read,将等待数据就绪和将数据拷贝到用户缓冲区两个步骤的工作全部交给操作系统来完成。当操作系统完成两个步骤以后,直接通知上层用户去用户缓冲区中读取数据即可。

也就是,进程不需要再考虑数据的IO,而是将其全权交给操作系统完成。

6.一些概念的解释

什么是同步IO和异步IO?

同步和异步的区别在于消息通信的机制。

所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回,但是一旦调用返回,就得到返回值了。

换句话说就是,调用者在主动等待这个调用的结果。

而异步则是调用开始执行后直接返回,调用者不会立刻得到结果,而是在调用返回后,被调用者通过状态、信号等通知调用者或通过回调函数处理。

话句话说,就是把事情交给了其他应用去做,自己只根据通信数据接收处理结果。

线程同步和同步IO有什么关系?

我们在讲解Linux线程时也提到了同步。

线程同步表示多个线程同时对临界资源进行操作时,系统为了保证没有线程处于饥饿状态,会以一定的顺序安排各个线程的执行顺序。

而同步IO表示处理数据的进程本身是否全权参与IO过程。

也就是,同步IO和线程同步之间,除了都有同步这个词之外没有任何关系。

三、非阻塞IO的代码实现

1.fcntl

int fcntl(int fd, int cmd, ... /* arg */ );

头文件:unistd.h、fcntl.h

功能:修改文件描述符的属性或对其进行其他操作。

参数:int fd需要操作的文件描述符。int cmd表示对描述符的操作。...表示可变参数列表,传入的cmd不同,参数也不同

返回值:成功返回非-1的值,失败返回-1。

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).

我们只使用第三个功能,即获取/设置文件状态标记,可将一个文件描述符设置为非阻塞。我们写一个SetNonBlock函数支持该功能。

//将文件描述符设为非阻塞
void SetNonBlock(int fd)
{
    int fl = fcntl(fd, F_GETFL);//获取文件描述符的标志,该标志是一个位图结构
    if(fl < 0)//获取失败
    {
        std::cerr << "fctnl:" << strerror(errno) << std::endl;//打印错误码
    }
    else
    {
        fcntl(fd, F_SETFL, fl | O_NONBLOCK);//将该文件描述符设为非阻塞
    }
}

2.实现工具类

util.hpp

#pragma once
#include<iostream>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>

//将文件描述符设为非阻塞
void SetNonBlock(int fd)
{
    int fl = fcntl(fd, F_GETFL);//获取文件描述符的标志,该标志是一个位图结构
    if(fl < 0)//获取失败
    {
        std::cerr << "fctnl:" << strerror(errno) << std::endl;//打印错误码
    }
    else
    {
        fcntl(fd, F_SETFL, fl | O_NONBLOCK);//将该文件描述符设为非阻塞
    }
}

void print_work()
{
    std::cout << "I am working. ";
}

3.实现主程序

由于我们从标准输入流(文件描述符为0)中读取数据,所以只要我们敲击键盘输入文字,就相当于向标准输入流中写入数据。

main.cc

#include"util.hpp"
#include<iostream>

int main()
{
    SetNonBlock(0);//设置文件描述符为非阻塞
    while(1)
    {
        char buffer[1024];
        ssize_t n = read(0, buffer, sizeof(buffer)-1);//读取数据
        if(n > 0)//读到了数据
        {
            buffer[n] = '\0';
            std::cout << buffer << std::endl;
        }
        else if(n == 0)//读到了结尾
        {
            std::cout << "read end" << std::endl;
            break;
        }
        else//n等于-1有两种情况,一种是读取出错,另一种是数据还没准备好,read只能按-1返回
        {
            if (errno == EAGAIN)//错误码为EAGAIN表示数据还没有准备好
            {
                //std::cout << "我没错, 只是没有数据" << std::endl;
                print_work();//程序继续执行自己的事
            }
            else if (errno == EINTR)//错误码为EINTR表示读取时进程收到了信号,需要进行处理,读取就被暂时打断了。
            {
                continue;//继续循环
            }
            else//这次就是出错了,打印错误码就可以了
            {
                std::cout << " errno: " << strerror(errno) << std::endl;
                break;
            }
        }
        sleep(1);
    }
    return 0;
}

此时即使屏幕上输出的数据乱成一团,但是并不会影响输入标准输入流的信息。

换句话说,非阻塞等待时,程序确实可以继续执行自己的流程。

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

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

相关文章

openGauss学习笔记-115 openGauss 数据库管理-设置安全策略-设置密码安全策略

文章目录 openGauss学习笔记-115 openGauss 数据库管理-设置安全策略-设置密码安全策略115.1 操作步骤 openGauss学习笔记-115 openGauss 数据库管理-设置安全策略-设置密码安全策略 115.1 操作步骤 用户密码存储在系统表pg_authid中&#xff0c;为防止用户密码泄露&#xff…

HJ72 百钱买百鸡问题

题目&#xff1a; HJ72 百钱买百鸡问题 题解&#xff1a; 暴力枚举。 鸡翁一值钱五&#xff0c;鸡翁最多买20只鸡母一值钱三&#xff0c;鸡母最多买100/3只鸡雏三值钱一&#xff0c;鸡雏最多买100只 private void buyChicken() {int totalCount 100;int totalMoney 100;f…

centos关闭Java进程的脚本

centos关闭Java进程的脚本&#xff0c;有时候服务就是个jar包&#xff0c;关闭程序又要找到进程ID&#xff0c;在kill掉&#xff0c;麻烦&#xff0c;这里就写了个脚本 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.脚本如下 #!/bin/bash ps -ef | grep java | gre…

Selenium安装WebDriver Chrome驱动(含 116/117/118/119/120/)

1、确认浏览器的版本 在浏览器的地址栏&#xff0c;输入chrome://version/&#xff0c;回车后即可查看到对应版本 2、找到对应的chromedriver版本 2.1 114及之前的版本可以通过点击下载chromedriver,根据版本号&#xff08;只看大版本&#xff09;下载对应文件 2.2 116版本…

第二章:input partitioning

文章目录 Input partitioninginput partitioning 的目的computational / domain faults等价类&#xff08;equivalence classes&#xff09;input conditions & valid / invalid inputspartitioning and equivalence classes等价类划分的原则 白盒 - Domain testing复合谓词…

【电路笔记】-谐波

谐波 文章目录 谐波1、概述2、频谱分析3、已知信号4、未知信号5、总结 周期性信号并不总是完美的正弦模式&#xff0c;例如我们之前有关 正弦波的文章之一中介绍的那样。 有时&#xff0c;信号确实可以是简单正弦波的叠加&#xff0c;它们被称为复杂波形。 在本文中&#xff0…

【C++深入浅出】STL之string用法详解

目录 一. 前言 二. STL概要 2.1 什么是STL 2.2 STL的六大组件 2.3 STL的缺陷 三. string类概述 3.1 什么是string类 3.2 为什么要使用string类 四. string类的使用 4.1 包含头文件 4.2 构造函数 4.3 赋值运算符重载 4.4 容量操作 4.5 访问/遍历操作 4.6 查找修改…

JavaEE平台技术——Spring和Spring Boot

JavaEE平台技术——Spring和Spring Boot 1. 控制反转1.1. IoC是什么1.2. IoC能做什么1.3. IoC和DI 2. SpringBean对象定义3. Spring容器4. SpringBoot 在观看这个之前&#xff0c;大家请查阅前序内容。 &#x1f600;JavaEE的渊源 &#x1f600;&#x1f600;JavaEE平台技术——…

基于SSM的网吧计费管理系统(有报告)。Javaee项目,ssm项目。

演示视频&#xff1a; 基于SSM的网吧计费管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通…

基于SSM的教学管理系统(有报告)。Javaee项目。

演示视频&#xff1a; 基于SSM的教学管理系统&#xff08;有报告&#xff09;。Javaee项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringMvc My…

自动化测试之争:code vs codeless

在TesterHome看到的一个话题&#xff0c;当我们选择做自动化时是否需要code 或者codeless。 code方案 用code去做自动化&#xff0c;实现过程就是拿个IDE撸代码。 python pytest/unittest appium/selenium/requests ... Java Junit/testNG appium/selenium/requests .…

go测试库之apitest

前言 使用go语言做开发差不多快一年了&#xff0c;主要用来写后端Web服务&#xff0c;从一开始吐槽他的结构体&#xff0c;比如创建个复杂的JSON格式数据&#xff0c;那是相当的痛苦。还有 err 处理写的巨麻烦。 当然&#xff0c;go 也有爽的地方&#xff0c;创建个线协程简直…

Compose-Multiplatform在Android和iOS上的实践

本文字数&#xff1a;4680字 预计阅读时间&#xff1a;30分钟 01 简介 之前我们探讨过KMM&#xff0c;即Kotlin Multiplatform Mobile&#xff0c;是Kotlin发布的移动端跨平台框架。当时的结论是KMM提倡将共有的逻辑部分抽出&#xff0c;由KMM封装成Android(Kotlin/JVM)的aar和…

Spring Cloud分布式缓存

目录 单点Redis Redis数据持久化 RDB持久化 bgsave细节 RDB的缺点 AOF持久化 AOF的问题 RDB与AOF对比 搭建Redis主从架构 数据同步原理 全量同步 增量同步 主从同步优化 Redis哨兵 集群检测 选举主节点 故障转移 搭建哨兵集群 RedisTemplate的哨兵模式 单点…

ConcurrentHashMap是如何实现线程安全的

目录 原理&#xff1a; 初始化数据结构时的线程安全 put 操作时的线程安全 原理&#xff1a; 多段锁cassynchronize 初始化数据结构时的线程安全 在 JDK 1.8 中&#xff0c;初始化 ConcurrentHashMap 的时候这个 Node[] 数组是还未初始化的&#xff0c;会等到第一次 put() 方…

【Java】三种方案实现 Redis 分布式锁

序言 setnx、Redisson、RedLock 都可以实现分布式锁&#xff0c;从易到难得排序为&#xff1a;setnx < Redisson < RedLock。一般情况下&#xff0c;直接使用 Redisson 就可以啦&#xff0c;有很多逻辑框架的作者都已经考虑到了。 方案一&#xff1a;setnx 1.1、简单实…

PDF 表单直接保存到您的文档中--TX Text Control

TX Text Control .NET Server for ASP.NET Document Viewer 32.0.2 允许用户保存包含已填写表单字段的文档&#xff0c;从而更轻松地协作和共享信息。 TX Text Control .NET Server for ASP.NET 是一个适用于 ASP.NET 和 ASP.NET Core 的综合服务器端文档处理库。功能包括 PDF …

程序员笔记本电脑选 windows 还是 MAC

计算机选择是每个进入 IT 行业同学的第一个重要选择&#xff0c;那么你是怎么选择的呢&#xff1f; 选择操作系统&#xff08;Windows还是macOS&#xff09;取决于程序员的需求、偏好和工作流程。每个操作系统都有其优点和缺点&#xff0c;下面将分别讨论它们&#xff0c;以帮助…

volatile-无原子性案例详解

package com.nanjing.gulimall.zhouyimo.controller;import java.util.concurrent.TimeUnit;/*** author zhou* version 1.0* date 2023/11/5 7:56 下午*/ class MyNumber{int number;public synchronized void add(){number;} } public class VolatileNoAtomicDemo {public st…

gcc -static 在centos stream8 和centos stream9中运行报错的解决办法

gcc -static 在centos stream8 和centos stream9中运行报错的解决办法&#xff1a; 报/usr/bin/ld: cannot find -lc 我们下载glibc-static&#xff1a; 选择x86_64的。 还有一个是libxcrypt-static&#xff0c;依旧在这个网站里搜。 rpm -ivh glibc-static-2.28-239.el8.x…