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

【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;}}}
}

相关文章:

  • Zookeeper HA集群搭建
  • 使用vue2 开发一个纯静态的校园二手交易平台-前端项目练习
  • 区块链:去中心化应用(DApp)开发全流程解析
  • 全栈量子跃迁:当Shor算法破解RSA时,我们如何用晶格密码重构数字世界的信任基岩?
  • 计算机三大主流操作系统的前世今生 - Linux|macOS|Windows
  • 解锁现代生活的健康密码
  • 设计并实现一个基于 Java + Spring Boot + MySQL 的通用多租户权限系统
  • 榕壹云国际版短剧系统:基于Spring Boot+MySQL+UniApp的全球短剧创作平台
  • Golang | Builder模式
  • 神经网络(自己记录)
  • Java 设计模式心法之第25篇 - 中介者 (Mediator) - 用“中央协调”降低对象间耦合度
  • PDF嵌入隐藏的文字
  • Rule.resourceQuery(通过路径参数指定loader匹配规则)
  • PH热榜 | 2025-04-26
  • 百度Create大会深度解读:AI Agent与多模态模型如何重塑未来?
  • 如何删除Google Chrome中的所有历史记录【一键清除】
  • 【音视频】SDL窗口显示
  • Vue.js 核心特性解析:响应式原理与组合式API实践
  • IDEA 连接 Oracle 数据库
  • 《代码整洁之道》第7章 错误处理 - 笔记
  • 四川在浙江公开招募200名退休教师,赴川支教帮扶
  • 解放日报头版头条:“五个中心”蹄疾步稳谱新篇
  • 新任浙江省委常委、杭州市委书记刘非开展循迹溯源学习调研
  • QFII一季度现身超300家公司:持有南京银行市值最高,5家青睐立航科技
  • 仅退款正式成历史?仅退款究竟该不该有?
  • 俄总统助理:普京与美特使讨论了恢复俄乌直接谈判的可能性