首页 > 其他分享 >五种IO模型

五种IO模型

时间:2024-08-17 23:26:33浏览次数:11  
标签:std 异步 调用 模型 阻塞 五种 IO include

目录

一、五种IO模型

(一)阻塞IO

(二)非阻塞IO

(三)信号驱动IO

(四)IO多路转接

(五)异步IO

二、高级IO重要概念

(一)同步通信与异步通信

(二)阻塞与非阻塞

理解这四者的关系


在进行网络编程或文件操作时,IO模型的选择对程序的性能和效率有着重要的影响。本文将介绍五种IO模型,并详细讨论非阻塞IO的相关内容。

IO有两个阶段:数据准备、数据读写,两个完整的阶段我们就成为一个IO过程。

通俗一点: IO = 等待 + 拷贝

一、五种IO模型

(一)阻塞IO

在内核将数据准备好之前,系统调用会一直等待。所有的套接字默认都是阻塞方式,这是最常见、也是之前使用最多的IO模型。

当调用IO接口时,本质就是在做用户层和内核层的数据拷贝,但是调用的时刻大概率是不一定满足就绪条件的,此时就会阻塞等待,直到读/写条件满足。

(二)非阻塞IO

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

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

系统调用接口:

  • fcntl

函数原型:int fcntl(int fd, int cmd,... /* arg */ );,传入的cmd的值不同,后面追加的参数也不相同。此处使用第三种功能,获取/设置文件状态标记,就可以将一个文件描述符设置为非阻塞。

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

具体示例:

  • 非阻塞轮询方式读取标准输入

示例代码:

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <cstdlib>
#include <cerrno>

// 对指定的fd设置非阻塞
void SetNonBlock(int fd)
{
    int fl = fcntl(fd, F_GETFL);
    if(fl < 0)
    {
        std::cerr << "fcntl error" << std::endl;
        exit(0);
    }
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}


int main()
{
    SetNonBlock(0);
    while(true)
    {
        char buffer[1024];
        ssize_t s = read(0, buffer, sizeof(buffer)-1); // sizeof(buffer)-1
        if(s > 0)
        {
            buffer[s] = 0;
            std::cout << "echo# " << buffer << std::endl; 
        }
        else if(s == 0)
        {
            std::cout << "end stdin" << std::endl;
            break;
        }
        else
        {
            // 非阻塞等待, 如果数据没有准备好,返回值会按照出错返回, s == -1
            // 数据没有准备好 vs 真的出错了 : 处理方式一定不是一样的。 s无法区分!
            // 数据没有准备好,算读取错误吗?不算。read,recv以出错的形式告知上层,数据还没有准备好
            if(errno == EWOULDBLOCK)
            {
                std::cout << "OS的底层数据还没有就绪, errno: " << errno << std::endl;
                // 做其他事情了
            }
            else if(errno == EINTR)
            {
                std::cout << "IO interrupted by signal, try again" << std::endl;
            }
            else
            {
                std::cout << "read error!" << std::endl;
                break;
            }
        }
        sleep(1);
    }
}

对于设置为非阻塞的文件描述符,读取的返回值 s :

  • s > 0: 读取成功,实际读取了 s 字节
  • s == 0: 读到结尾,正常结束。
  • s < 0: 数据并未准备好 / 读取出错
  1. 数据并未准备好:设置错误码为 EWOULDBLOCK 或 EAGAIN (本质是同一个数字)
  2. 当正在读取数据时收到信号被打断,设置错误码为 EINTR
  3. 排除前两种情况之后,则是真的出错了。

所以对于返回值小于0的情况需要再进一步判断。

以上是对于非阻塞的fd读取的示例,写入也是同理,同样把写条件未就绪归并为返回值小于0,并设置错误码。

(三)信号驱动IO

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

在信号驱动 I/O 中,进程首先使用 sigaction 系统调用安装一个信号处理函数,并通过 fcntl 系统调用将文件描述符设置为非阻塞和信号驱动模式。

当内核中数据准备好时,会发送一个信号给进程。进程在收到信号后,再进行实际的 I/O 操作。

这种方式的优点在于:进程不必阻塞等待 I/O 操作完成,可以在等待数据准备好的同时执行其他任务,提高了 CPU 的利用率。

简单示例代码:

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>

void signalHandler(int signum) {
    std::cout << "Signal received. Performing I/O operation." << std::endl;
}

int main() {
    int fd = open("test.txt", O_RDONLY | O_NONBLOCK);

    if (fd == -1) {
        std::cerr << "Error opening file" << std::endl;
        return 1;
    }

    // 设置文件描述符为信号驱动 I/O
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC);

    // 安装信号处理函数
    signal(SIGIO, signalHandler);

    // 设置当前进程为接收信号的进程
    fcntl(fd, F_SETOWN, getpid());

    char buffer[1024];
    while (true) {
        std::cout << "Doing other work..." << std::endl;
        read(fd, buffer, sizeof(buffer));
        sleep(1);
    }

    close(fd);

    return 0;
}

整个程序的目的是在执行其他工作的同时,能够响应与文件 I/O 相关的信号,并进行相应的处理。

例如,如果在文件有可读数据等情况下,会发送 SIGIO 信号,从而触发 signalHandler 函数的执行。

(四)IO多路转接

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

多路转接:select、poll、epoll,三者在IO种的定位都是相同的,都是在等待一堆文件描述符的条件就绪,只是原理和具体接口的使用不同,epoll的效率是最优的,后续文章再具体讲解多路转接部分。

(五)异步IO

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

也就是通过调用特殊的API,使得等待和拷贝的过程都由内核完成,进程只用坐享其成的使用数据即可,因为IO具体过程的两步(等待、拷贝)都没有参与,所以是异步IO。

以下是一个简单的 C++ 异步 I/O 示例代码,

使用了 std::future 和 std::async 来模拟异步文件读取操作:

#include <iostream>
#include <future>
#include <fstream>

// 异步读取文件的函数
std::string asyncReadFile(const std::string& filename) {
    std::ifstream file(filename);
    std::string content;
    if (file.is_open()) {
        std::string line;
        while (std::getline(file, line)) {
            content += line + '\n';
        }
        file.close();
    }
    return content;
}

int main() {
    std::string filename = "example.txt";
    // 启动异步任务
    std::future<std::string> future = std::async(asyncReadFile, filename);

    std::cout << "Doing other work while reading the file asynchronously..." << std::endl;

    // 获取异步任务的结果
    std::string fileContent = future.get();

    std::cout << "File content: " << fileContent << std::endl;

    return 0;
}

小结

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

二、高级IO重要概念

(一)同步通信与异步通信

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

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

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

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

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

(二)阻塞与非阻塞

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

  • 阻塞调用:指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
  • 非阻塞调用:指在不能立刻得到结果之前,该调用不会阻塞当前线程。
理解这四者的关系

可以用“妖怪蒸唐僧”的例子来理解这四者的关系:

  • 同步阻塞:就像妖怪蒸唐僧时,一直守在蒸笼旁边,直到唐僧蒸熟(得到结果)才离开,这期间什么也不干,一直等待。
  • 同步非阻塞:还是妖怪蒸唐僧,但是会时不时去看看唐僧熟了没(轮询),没熟就去干别的事,熟了就回来处理。
  • 异步阻塞:这种情况比较奇怪,一般不太会出现,就像妖怪告诉别人,等唐僧熟了通知我,然后就在蒸笼旁边等着。
  • 异步非阻塞:妖怪告诉别人,等唐僧熟了通知我,然后就去干别的事了,等收到通知再回来处理。

标签:std,异步,调用,模型,阻塞,五种,IO,include
From: https://blog.csdn.net/weixin_73567058/article/details/141287128

相关文章

  • DataWhale AI夏令营-大模型微调-学习笔记3
     Task1:从零入门大模型微调一、问题概述从零入门大模型微调是Datawhale2024年AI夏令营第四期的学习活动(“大模型技术”方向),基于讯飞开放平台“星火大模型驱动阅读理解题库构建挑战赛”开展的实践学习。学习内容:基于讯飞大模型定制训练平台和spark-13b微调模型,生成高考......
  • Problems in Mathematical Analysis (American First Edition)
    https://www.amazon.com/Problems-Mathematical-Analysis-Boris-Demidovich/dp/B0007BNL1W/ ProblemsinMathematicalAnalysis(AmericanFirstEdition) Reviews:https://www.amazon.com/gp/customer-reviews/R225A0UUURIG9B/ref=cm_cr_dp_d_rvw_ttl?ie=UTF8&ASI......
  • LangGraph Studio:首款智能体(agent)IDE
    0前言LangGraphStudio提供了一个专门的智能体IDE,用于可视化、交互和调试复杂的智能体应用程序。本文来了解如何在桌面使用。LLM为新型智能体应用程序的发展铺平了道路——随这些应用程序演进,开发它们所需工具也必须不断改进。今天推出的LangGraphStudio——首款专为Agent开......
  • Object Detection: Non-Maximum Suppression (NMS)
    ObjectDetection:Non-MaximumSuppression(NMS)https://kikaben.com/object-detection-non-maximum-suppression/ObjectdetectionmodelslikeYOLOv5andSSDpredictobjects’locationsbygeneratingboundingboxes(showninbluerectanglesbelow).However,......
  • Stable Diffusion概念
    stablediffusionmodels:是一类神经网络模型的统称,主要是使用扩散过程来生成高质量图片controlNet:automatic1111:checkpoint:一般也称model,是必须的主模型,文件体积GB级别,文件后缀通常是ckpt或者safetensorslora:是一种stablediffusionmodel,是通过保存的少量的训练参数来微调......
  • 集合IO流
    packagecom.shujia.day17.ketang;/*键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低存入文本文件*/importjava.io.BufferedReader;importjava.io.BufferedWriter;importjava.io.FileWriter;importjava.io.IOException;import......
  • Datawhale X 魔搭 AI夏令营-大模型应用(二)
    一.首先重大突破是完成了向量数据库的搭建二.初次的版本由于每次运行都要下载yuan模型,且每次都要运行模型嵌套函数,导致模型运行卡顿,以下是初版三.后来我们组想到可以先把模型下载到本地,然后优化代码,把两个代码文件合二为一,成功的提了速度,期间还有懒加载的思考,还有max_new_tok......
  • Axios
    1.Axios1.1异步请求发展史1.1.1传统的Ajax传统的Ajax请求是基于XMLHttpRequest(XHR)对象。可以直接使用。但 是使用起来配置较为麻烦,实际开发中使用非常少,在MVC时代通常使用的 是JQuery-Ajax。相对于传统的Ajax现在使用更多的是Fetch请求。1.1.2JQuery-AjaxJQuery-Ajax在前......
  • P5642 人造情感(emotion)
    link考虑\(W(U)\)怎么求。定义\(f_x\)表示只考虑所有在\(x\)子树内的路径时最大收益,\(sum_x\)为只考虑\(x\)子树中路径,且钦定\(x\)不选的最大收益。\(g\)的转移显然:\(g_x=\sumf_{to}\)\(f\)转移考虑枚举\(\text{lca}=x\)的所有路径\((u,v,w)\),有:\(f_x\longle......
  • 登录 k8s-Dashboard 显示 Your connection is not private
    目录一、背景二、解决方案一、背景部署好kubernetes-Dashboard后使用master节点的ip+port登录Dashboard显示Yourconnectionisnotprivate无论是Edge还是GoogleChrome都是这样的情况二、解决方案点击网页空白处,英文输入法输入:thisisunsafe即可正常访问......