5种IO模型

目录

一、认识IO

什么是IO? Input(输入)和Output(输出)。

冯诺依曼体系结构中,数据从输入设备拷贝到内存,经过处理后,再从内存拷贝到输出设备。现实情况中,数据并不是那么流畅的进行拷贝操作,而是会进行"等"。比如系统调用recv,当读取条件不满足时,它就会阻塞,当读取条件满足了,拷贝数据并返回结果。

所以总结一下,recv一共就做了两件事:等+拷贝 => IO=等+拷贝

其他系统调用也是一样,比如send,当发送缓冲区满了,就会阻塞,直到有空间腾出来,才能进行发送数据。

什么是高效IO? 单位时间内,等的比重越低,IO效率越高
高效的IO代码: 减少IO的比重或者等的时间利用起来

二、5种IO模型

1️⃣阻塞IO

内核准备好数据之前,系统调用会一直等待

2️⃣非阻塞IO

如果系统调用一直长时间等下去,对CPU来说是较大的浪费。

如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回EWOULDBLOCK 错误码。返回的间隙应用进程可以做别的事情。

3️⃣信号驱动IO

数据准备好了,内核发送信号通知应用进程可以进行IO操作了。

4️⃣多路转接IO

与阻塞IO类似,区别在于多路转接IO可以同时等待多个fd的就绪状态。

5️⃣异步IO

由内核完成数据的拷贝,然后通知应用进程来处理数据(注意:信号驱动IO是发送信号告诉应用进程什么时候来拷贝数据)

阻塞IO与非阻塞IO

等的方式不一样,前者干等,后者轮询式的等待,期间可以做别的事情

同步IO与异步IO

阻塞IO、非阻塞IO、信号驱动IO和多路转接IO是同步IO,最后一个是异步IO。同步IO应用程序的系统调用都有参与IO的操作,异步IO拷贝数据时是内核来完成的

谁最高效?

多路转接IO最高效,栗子:200个人(为一个整体)钓鱼与一个人钓鱼,200个人中任意一个人钓上鱼的概率比一个人钓上鱼的概率要大

三、非阻塞IO代码

系统调用:fcntl

设置非阻塞:

cpp 复制代码
void SetNonblock(int fd)
{
    int fl = fcntl(fd, F_GETFL);
    if(fl < 0)
    {
        return;
    }
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}

1️⃣阻塞IO代码

cpp 复制代码
#include "Comm.hpp"
int main()
{
    char buffer[1024];
    while (true)
    {
        ssize_t s = read(0, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            cout << "echo# " << buffer << endl;
        }
        else
        {
            cout << "读取错误..." << endl;
            sleep(1);
        }
    }
    return 0;
}

没有数据等待用户输入,有数据就打印数据

2️⃣非阻塞IO代码

cpp 复制代码
#include "Comm.hpp"
int main()
{
    char buffer[1024];
    SetNonblock(0);// 设置非阻塞
    while (true)
    {
        ssize_t s = read(0, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            cout << "echo# " << buffer << endl;
        }
        else
        {
            cout << "读取错误..." << endl;
            sleep(1);
        }
    }
    return 0;
}

怎么知道是IO条件不就绪,还是读取错误?

打印返回值:

cpp 复制代码
#include "Comm.hpp"
int main()
{
    char buffer[1024];
    SetNonblock(0);// 设置非阻塞
    while (true)
    {
        ssize_t s = read(0, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            cout << "echo# " << buffer << endl;
        }
        else
        {
            cout << "读取错误... , s: " << s << endl;
            sleep(1);
        }
    }
    return 0;
}

注意:IO条件不就绪和读取错误的返回值是一样的

打印错误码:

cpp 复制代码
#include "Comm.hpp"
int main()
{
    char buffer[1024];
    SetNonblock(0);// 设置非阻塞
    while (true)
    {
        ssize_t s = read(0, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            cout << "echo# " << buffer << endl;
        }
        else
        {
            cout << "读取错误... , s: " << s << " errno: " << errno << endl;
            sleep(1);
        }
    }
    return 0;
}

说明是底层IO条件不就绪导致的:

cpp 复制代码
#include "Comm.hpp"
int main()
{
    char buffer[1024];
    SetNonblock(0);// 设置非阻塞
    while (true)
    {
        ssize_t s = read(0, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            cout << "echo# " << buffer << endl;
        }
        else
        {
            if(errno == EWOULDBLOCK || errno == EAGAIN)
            {
                cout << "IO条件不就绪, 下次再试下" << endl;
                sleep(1);
                continue;
            }
            cout << "读取错误..."<< endl;
            sleep(1);
        }
    }
    return 0;
}

如果有输入数据,这些数据会被按顺序读取,按回车后通过显示器设备打印出来(显示器也有输入缓冲区和输出缓冲区)

相关推荐
洋不写bug1 分钟前
网络编程(二)TCP回显服务器与客户端通信详解
服务器·网络·tcp/ip·tcp·网络通信·javaee·回显服务器
Carson带你学Android4 分钟前
Gemini 4 Argon 发布:对 Android 开发者意味着什么?
android·ai编程
lisanmengmeng15 分钟前
NRPE 添加命令(四)
linux·运维·服务器
知识分享小能手20 分钟前
C学习教程,从入门到精通,C语言概述 —— 知识点详解(1)
c语言·开发语言·学习
想做小南娘,发现自己是女生喵28 分钟前
i.MX6ULL嵌入式Linux入门学习文档
linux·运维·学习
东木月29 分钟前
Windows C 盘清理指南:安全删除临时文件、缓存与 Prefetch
c语言·windows·安全
FL162386312929 分钟前
Siemens‑Automation‑License‑Manager & SIMATIC‑Automation‑Tool 下载安装教程
运维·服务器
垆边人似月.33 分钟前
日志时间窗口内的最大并发请求数(100分 / 滑动窗口 + 排序)
数据结构·c++·算法
此生决int1 小时前
Linux系列(04)——基础开发工具(上)软件包管理器、vim、gcc/g++
linux·服务器·vim
Huangjin007_1 小时前
【Linux 系统篇(二十六)】文件(三): 深入理解 “一切皆文件”、缓冲区
linux·运维·服务器