当前位置: 首页 > java >正文

【Linux网络与网络编程】13.五种 IO 模型

前言

在前面的学习中,有一个问题一直没有展开来说,即 IO 问题。 IO 到底有多少种方式呢?什么是高效的 IO 呢?

IO 本质上就是 INPUT 和 OUTPUT 。在网络中 INPUT 就是从网卡中获取数据,而 OUTPUT 就是向网卡中发送数据。 IO 可以理解为等+拷贝。当 IO 事件就绪时就可以进行拷贝了,所以所谓的高效的 IO 就是让 IO 大部分时间处于拷贝而减少等待的时间。

那么如何高效的IO呢?这就提出了五种IO模型来提高 IO 效率。

1. 五种 IO 模型概述

阻塞 IO

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

非阻塞 IO

非阻塞 IO 即在内核还未将数据准备好时,系统调用仍然会直接返回,并且返回 EWOULDBLOCK 错误码。

信号驱动 IO

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

多路转接 IO

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

异步 IO

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

针对上述 IO 方式我们进行分析:

1. 阻塞 IO vs 非阻塞 IO

IO = 等 + 拷贝。它们等的方式不同,但是 IO 效率实际上是相同的。平常所说的非阻塞 IO 的效率高是因为它可以在等的时候做其他的事情。

2. 这五种 IO 模型哪个效率最高

当然是多路转接 IO 了,因为它能接收到数据就绪的概率更大了。

3. 信号驱动 IO 特点是什么?效率如何?

它逆转了获取就绪事件的方式,效率同阻塞 IO 和非阻塞 IO 相同,因为它们只是等的方式不同而已。

4. 同步 IO vs 异步 IO

我们介绍的前四种 IO 方式是同步 IO。同步 IO 和异步 IO 的本质区别就是有没有参与 IO 的过程。

针对上述的五种 IO 模型,我们会具体介绍非阻塞 IO 和多路转接 IO ,其中本篇博客的下一部分介绍非阻塞 IO ,下一篇博客将具体介绍多路转接 IO 。

2. 非阻塞 IO

其实之前学习过的很多函数都可以通过设置标记位的方式来实现非阻塞,但是Linux下一切皆文件,故而就产生了更为通用的设置方式,即对文件描述符进行操作。

#include <fcntl.h>
int fcntl(int fd, int op, ... /* arg */ );// 参数: 
//     fd:需要被操作的文件描述符
//     op:要进行的设置操作
//        F_DUPFD: 复制一个现有的描述符
//        F_GETFD 或 F_SETFD:获得/设置文件描述符标记
//        F_GETFL 或 F_SETFL:获得/设置文件状态标记
//        F_GETOWN 或 F_SETOWN:获得/设置异步 I/O 所有权
//        F_GETLK 获 F_SETLK 或 F_SETLKW:获得/设置记录锁
// 返回值:
//    成功的话依赖于所请求的内容,失败的话返回 -1

非阻塞如果不输入,数据就不会就绪,并以出错的形式返回。但是 read 不是有读取错误吗,两者如何区分呢?

如果读取错误的话就会被设置错误码 errno ,里面会有更详细的错误信息,但是如果是底层数据没有就绪的话就会触发11号错误 EAGAIN(EWOULDBLOCK)。

补充知识:

read 的阻塞是调用系统调用而进入了浅层睡眠 s 状态,而 s 状态收到信号也可能会被叫醒的!这样的情况错误码就是 EINTR 。

接下来我们写一段demo代码:

#include <iostream>
#include <cstdio>
#include <string>
#include <cerrno>
#include <unistd.h>
#include <fcntl.h>void SetNonBlock(int fd)
{// 获取得到文件描述符int fl = fcntl(fd, F_GETFL);if (fl < 0){perror("fcntl");return;}// 将文件描述符的属性置为非阻塞fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}int main()
{std::string tips = "Please Enter# ";char buffer[1024];SetNonBlock(0);while (true){write(0, tips.c_str(), tips.size());int n = read(0, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;std::cout << "echo# " << buffer << std::endl;}else if (n == 0){std::cout << "read file end!" << std::endl;break;}else{if (errno == EAGAIN || errno == EWOULDBLOCK){std::cout << "底层数据,没有就绪" << std::endl;sleep(1);continue;}else if (errno == EINTR){std::cout << "被中断,从新来" << std::endl;sleep(1);continue;}else{std::cout << "read error: " << n << ", errno: " << errno << std::endl;}}}
}
http://www.xdnf.cn/news/2520.html

相关文章:

  • AIGC(生成式AI)试用 32 -- AI做软件程序测试 3
  • git提交规范记录,常见的提交类型及模板、示例
  • 【音视频】SDL简介
  • 算法题(135):唯一的雪花
  • 大数据系列 | 日志数据采集工具Logstash的架构分析及应用
  • 微信小程序导航栏
  • C++STL(九) :bitset的介绍与使用
  • MCP介绍与使用
  • 第二部分:网页的妆容 —— CSS(上)
  • OpenSSH配置连接远程服务器MS ODBC驱动与Navicat数据库管理
  • 神经网络预测评估机制:损失函数详解
  • adb devices 报权限错误
  • 文件缓冲区(IO与文件 ·III)(linux/C)
  • 使用 malloc 函数模拟开辟一个 3x5 的整型二维数组
  • 基于QT(C++)实现(GUI)旅行查询与模拟系统
  • Python3 (13)循环语句
  • Java SE(3)——程序逻辑控制,输入输出
  • MySQL的锁(InnoDB)【学习笔记】
  • PlatformIO 入门学习笔记(二):开发环境介绍
  • Matlab算例运行
  • MCU ADC参考电压变化怎么办?
  • JS 中call、apply 和 bind使用方法和场景
  • 犬面部检测数据集VOC+YOLO格式987张1类别
  • ST-LINK/V2调试仿真器的接口定义
  • 计算机组成原理系列3--存储系统
  • 【QT】QT多线程
  • PMO 阶段性工作成果报告
  • 【C++QT】Layout 布局管理控件详解
  • STM32标准库和HAL库SPI发送数据的区别-即SPI_I2S_SendData()和HAL_SPI_Transmit()互换
  • 2025系统架构师---事件驱动架构