📝前言:

这篇文章我们来讲讲Linux五种IO模型 + 非阻塞IO

  1. 五种IO模型
  2. 非阻塞IO
    • fcntl
    • 非阻塞轮询模版

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记C语言入门基础python入门基础C++刷题专栏


一,五种IO模型

IO = 等 + 拷贝
I/O 效率是 “减少等待” 和 “优化拷贝” 共同决定的。但在硬件相同,拷贝次数相同的场景下,减少等待时间是提高IO效率的关键。

  • 阻塞IO:数据到来前,进程一直在read等,直到数据到来
    在这里插入图片描述

  • 非阻塞IO:如果内核还未将数据准备好, 系统调用仍然会直接返回, 并且返回EWOULDBLOCK 错误码
    在这里插入图片描述

    • 非阻塞 IO 往往需要程序员循环的方式反复尝试读写文件描述符, 这个过程称为轮询. 这对 CPU 来说是较大的浪费, 一般只有特定场景下才使用.
  • 信号驱动 IO:内核将数据准备好的时候, 使用 SIGIO 信号通知应用程序进行 IO操作。(属于同步IO,因为拷贝是自己完成的)
    在这里插入图片描述

  • IO 多路转接:同时等待多个文件描述符的就绪状态。
    在这里插入图片描述

  • 异步 IO:由内核在数据拷贝完成时, 通知应用程序(异步IO不参与 等 + 拷贝的任意一个过程)
    在这里插入图片描述

同步 vs 异步

  • 同步IO:参与等 or 拷贝中任意一个阶段(调用方需要主动等待结果就绪,或要亲自处理结果)。同步通信:参与调用 or 等待返回结果任意一个阶段,调用返回,结果跟着返回。
  • 异步:不参与任意一个阶段(调用方无需等待结果,由 “第三方”等待结果并处理,处理完后再通知调用方“完成了”)。

二,非阻塞IO

1. fcntl

一个文件描述符,默认都是阻塞 IO。fcntl允许我们设置文件描述符的标记位,让我们可以把文件描述符设置成非阻塞IO
在这里插入图片描述

  • cmd:命令,它决定了 fcntl 函数要执行的操作类型。不同的 cmd 对应不同的功能,也对应不同的返回值例如(主要用到的):
    • F_GETFL获取文件的状态标志(比如是否为非阻塞模式等)。
    • F_SETFL:设置文件的状态标志。
  • ... /* arg */:可变参数,根据 cmd 的不同,这个参数的意义和类型也不同。例如,当 cmdF_SETFL 时,arg 用于传递要设置的标志。
    • 标记有:O_APPENDO_NONBLOCK…(本质是设置了特定比特位的宏)
    • 一般我们可以先通过F_GETFL获取到原来的标志(返回值是一个位图),然后再F_SETFL原来标志 | 新标志

2. 非阻塞轮询模版

以轮询方式读取标准输入为例(标准输入0,默认是非阻塞输入的)

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

void SetNoBlock(int fd)
{
    int fl = fcntl(0, F_GETFL);
    if(fl < 0)
    {
        perror("fcntl error");
        return;
    }
    fcntl(0, F_SETFL, fl | O_NONBLOCK);
}
int main()
{
    SetNoBlock(0); // 设置非阻塞
    while(true)
    {
        char buffer[1024] = {0};
        // 从键盘读取数据,不会阻塞读。
        // 读不到就返回: <0 认为:"出错"。   
        // ==0是:写端已经关闭且无数据残留
        ssize_t read_size = read(0, buffer, sizeof(buffer) - 1);
        if(read_size < 0)
        {
            // 非阻塞无数据
            // 这两种"错误"代表暂无数据,重试可能成功
            // 两个错误码值相同,但是历史与系统的原因所以最好两个都判断
            if (errno == EAGAIN || errno == EWOULDBLOCK) 
            {
                printf("数据暂未就绪...\n"); // 这才是无输入时的正确提示
                sleep(1);
            }
            // 真正的错误
            else
            {
                perror("read error"); // 其他错误(如文件描述符无效)
            }
            continue;
        }
        printf("input: %s", buffer);
    }
    return 0;

}

运行效果:
在这里插入图片描述


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐