五种IO模型和非阻塞IO

一、几个前置知识:

1、在应用层的这些read和write函数,本质就是把数据从用户层写给操作系统,这些函数本质也

是拷贝函数。

2、对IO的理解应该是:IO时间=等待时间+拷贝时间

3、要进行拷贝,就必须判断条件是否成立(比如读就要看接受缓冲区是否有数据,写就要看发送

缓冲区是否有数据),而这个条件就被称为读写事件。

4、高效IO就是之单位时间内拷贝的数据量大,也就是说,在单位时间内,IO过程中,等的比重越小,IO的效率越高。

5、几乎所有提高IO效率的办法,都是减小等的比重。

二、五种IО模型

2.1、阻塞IO

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

2.2、非阻塞IO

如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回EWOULDBLOCK错误码。非阻塞IO往往需要通过循环的方式反复尝试读写文件描述符,这个过程称为轮询。这对CPU来说是较大的浪费,一般只有特定场景下才使用。

2.3、信号驱动IO

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

2.4、IO多路转接

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

2.5、异步IO

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

总结:

1、阻塞式IO、非阻塞式IO、信号驱动式IO、多路转接这四种都是同步IO。

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

3、阻塞式IO和非阻塞式IO在IO效率上是一样的,因为他们都在等,只不过等的方式不同而已。

4、同步IO和异步IO的区别是有没有直接参与IO的过程,直接参与IO就叫同步IO,不直接参与IO就叫异步IO ,异步IO知识发起IO,最后拿结果就行。

5、上述IO效率最高的是多路复用(多路转接)。

三、非阻塞IO

3.1fcntl

一个文件描述符,默认都是阻塞IO

函数原型如下:

传入的cmd的值不同,后面追加的参数也不相同。

fcntl函数有5种功能:

(1)复制一个现有的描述符(cmd=F_DUPFD)

(2)获得/设置文件描述符标记(Cmd=F_GETFD或F_SETFD)

(3)获得/设置文件状态标记(Cmd=F_GETFL或F_SETFL)

(4)获得/设置异步I/O所有权(Cmd=F_GETOWN或F_SETOWN)

(5)获得/设置记录锁(Cmd=F_GETLK,F_SETLK或F_SETLKW)

注意:用第三种功能,获取/设置文件状态标记,就可以将一个文件描述符设置为非阻塞。

3.2实现函数SetNoBlock

基于fcntl,实现一个SetNoBlock函数,将文件描述符设置为非阻塞。

cpp 复制代码
void SetNoBlock(int fd)
{
    int fl = fcntl(fd, F_GETFL);
    if (fl < 0)
    {
        perror("fcntl error");
        return;
    }
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
    cout << "set " << fd << " done" << endl;
}

使用F_GETFL将当前的文件描述符的属性取出来(这个属性是一个位图),然后再使用F_SETFL将文件描述符设置回去,设置回去的同时,加上一个O_NONBLOCK参数。

四、轮询方式读取标准输入

cpp 复制代码
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <cerrno>
#include <cstring>

using namespace std;

void SetNoBlock(int fd)
{
    int fl = fcntl(fd, F_GETFL);
    if (fl < 0)
    {
        perror("fcntl error");
        return;
    }
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
    cout << "set " << fd << " done" << endl;
}

int main()
{
    char buffer[1024];
    SetNoBlock(0);
    while (true)
    {
        cout << "please enter@" << endl;
        fflush(stdout);
        ssize_t n = read(0, buffer, sizeof(buffer));
        if (n > 0)
        {
            buffer[n] = 0;
            cout << "echo:" << buffer << endl;
        }
        else if (n == 0)
        {
            cout << "read done" << endl;
        }
        else
        {
            // 1、设置为非阻塞轮询,如果底层fd的数据没有就绪,recv/read/write/send等就会以出错的形式给我们返回
            // 2、有可能真的出错,有可能是底层资源未就绪,就通过errno错误码进行区分
            if (errno == EWOULDBLOCK)
            {
                cout << "0 fd data not ready, try again!!!" << endl;
                // 等待的时候可以做其他事情
                sleep(1);
            }
            else
            {
                // 出错了
                cerr << "read error, n =" << n << ", errno:" << errno << ", errno reason:" << strerror(errno) << endl;
            }
        }
    }
    return 0;
}

运行结果:

相关推荐
环流_1 小时前
nacos:负载均衡 3大核心操作
运维·nacos·负载均衡
m0_733565461 小时前
如何指定PHP版本运行phpMyAdmin_多版本共存配置
jvm·数据库·python
Bruce_Liuxiaowei2 小时前
AI攻防时间差:当漏洞发现速度碾压修复速度— 聚焦技术核心
网络·人工智能·网络安全·ai·系统安全
xcLeigh2 小时前
IoTDB JDBC 完整使用教程:连接、查询、批处理与字符集配置
开发语言·数据库·qt·iotdb·查询·批处理·连接
chunyublog2 小时前
数据挖掘环境搭建
数据库
阿洛学长2 小时前
CSDN、掘金、简书博客文章如何转为Markdown?
运维·数据库·架构·php·持续部署
方安乐2 小时前
交换机的自学机制
运维·服务器·网络
zuozewei2 小时前
国产化之达梦数据库性能优化方案
数据库·性能优化
哈哈浩丶2 小时前
存储相关知识①—通用NAND Flash 基础
linux·存储·nand