Linux I/O编程:I/O多路复用与异步 I/O对比

devtools/2024/11/13 11:44:48/

文章目录

0. 引言

本文将简要介绍 Linux 中的多路复用 I/O 和异步 I/O 模型,并编写示例代码简述。对于网络 I/O,特别是 UDP 和 TCP,多路复用 I/O(如 selectepoll())通常是更好的选择。而异步 I/O(如 libaio)只建议用在文件和块设备的 I/O 操作。

1. I/O 模型简介

Linux 提供了五种常见的 I/O 模型:阻塞 I/O、非阻塞 I/O、多路复用 I/O、信号驱动 I/O 和异步 I/O。

1.1 阻塞 I/O(Blocking I/O)

阻塞 I/O 是最简单的模型,进程会在 I/O 操作完成之前被挂起。适用于低并发、对性能要求不高的场景。

工作原理

  • 进程发起 I/O 操作,若数据不可用,进程会被阻塞,直到数据准备好。

1.2 非阻塞 I/O(Non-Blocking I/O)

非阻塞 I/O 允许进程继续执行其他任务,当数据不可用时,系统返回一个错误码(如 EAGAIN)。进程可以选择继续做其他事情,或者再次尝试 I/O 操作。

工作原理

  • 进程发起 I/O 操作后,如果数据不可用,系统返回错误码,进程继续执行其他任务。

1.3 信号驱动式 I/O(Signal-Driven I/O)

通过内核向进程发送信号通知数据准备情况,进程可以继续执行其他任务,直到收到信号时再处理数据。

适用场景:不希望主动轮询的应用,通常用于低延迟要求的系统或设备驱动

1.4 多路复用 I/O(I/O Multiplexing)

多路复用 I/O 允许一个进程同时监控多个 I/O 通道,避免因等待 I/O 完成而导致的阻塞。

工作原理

  • 通过 select()poll()epoll() 等系统调用,进程可以同时监控多个 I/O 通道,当某个通道就绪时,进程会被通知。
应用进程 内核 select 系统调用,阻塞 数据未准备 等待数据 数据准备好 返回可读条件 read 系统调用 成功响应 应用进程 内核

1.5 异步 I/O(Asynchronous I/O)

异步 I/O 的核心思想是,进程发起 I/O 操作后,系统立即返回,I/O 操作在后台完成。当操作完成时,系统会通知进程。

工作原理

  • 进程发起异步 I/O 操作,系统立即返回,I/O 在后台进行,完成时通知进程。
应用进程 内核 aio_read 返回 数据未准备 等待数据 数据准备好 通知操作完成 处理数据 应用进程 内核

2. 多路复用 I/O VS 异步 I/O 分析

2.1 多路复用 I/O

工作原理

多路复用 I/O 允许一个进程同时监控多个 I/O 通道。在 Linux 中,常用的系统调用包括 select()poll()epoll()

  • select():适合少量文件描述符,效率较低。
  • poll():与 select() 类似,但支持更多文件描述符。
  • epoll():高效地处理大量并发连接,采用事件驱动机制。

优劣势

多路复用允许单个进程处理多个 I/O 操作,减少线程切换的开销。然而,随着文件描述符数量的增加,轮询的开销也会增加。epoll() 通过事件驱动的方式,在大规模并发场景中表现优异。

2.2 异步 I/O

工作原理

异步 I/O 的关键是,进程发起 I/O 操作后,系统立即返回,后台继续执行 I/O 操作,而进程可以继续执行其他任务。当 I/O 操作完成时,系统通过回调函数、信号或其他机制通知进程。

Linux 中的异步 I/O 接口有两种主要实现方式:

  • POSIX AIO:通过 aio_readaio_write 提交异步 I/O 操作,操作完成后,进程可以通过 aio_erroraio_return 获取结果。需要注意的是,POSIX AIO 在某些 Linux 发行版中可能是基于用户空间的模拟,而不是真正的异步 I/O,这可能会影响其性能。
  • libaio:高效的异步 I/O 接口,直接与内核交互,减少线程上下文切换。libaio 特别适合磁盘和文件系统的异步块读写操作。

带来的挑战

  • 资源管理异步 I/O 需要额外的内存和控制块,可能导致内存泄漏或资源管理困难。
  • 线程调度异步 I/O 可能会引入线程切换和上下文切换的开销,尤其在大量小 I/O 操作时,性能可能下降。
  • 错误处理异步 I/O 错误处理较为复杂,特别是回调嵌套或并发操作时,状态同步问题尤为突出。

2.3 多路复用 I/O 与异步 I/O 的比较

  • 适用场景

    • 多路复用 I/O:适用于网络 I/O 场景,特别是 UDP 和 TCP。epoll() 在处理大量并发连接时表现优异。
    • 异步 I/O:适用于文件和块设备的 I/O 操作,如磁盘读写。libaio 在减少上下文切换和提高磁盘 I/O 性能方面效果显著。
  • 性能和复杂性

    • 多路复用 I/O:简单易用,适合大多数网络应用场景。epoll() 在高并发场景下性能优越。
    • 异步 I/O:虽然性能潜力大,但实现复杂,错误处理和资源管理较为困难。
  • UDP 无连接协议:UDP 本身是无连接协议,内核的非阻塞 I/O 和事件驱动模型已经非常高效。UDP 数据包的发送操作不涉及复杂的连接管理,使用非阻塞 I/O 或事件驱动模型(如 selectepoll)即可高效处理并发。

2.4 总结

对于网络 I/O,特别是 UDP 和 TCP,多路复用 I/O(如 selectepoll())通常是更好的选择。而异步 I/O(如 libaio)只建议用在文件和块设备的 I/O 操作。

3. 多路复用 I/O 和异步 I/O示例代码

3.1 多路复用 I/O 示例(select

这个例子演示了如何使用 select 实现多路复用 I/O,监听多个文件描述符(如标准输入和套接字)。

#include <iostream>
#include <unistd.h>
#include <sys/select.h>
#include <fcntl.h>
#include <string.h>int main() {fd_set read_fds;struct timeval timeout;// 创建标准输入的文件描述符集FD_ZERO(&read_fds);FD_SET(STDIN_FILENO, &read_fds);// 设置 select 的超时,单位是秒和微秒timeout.tv_sec = 5;timeout.tv_usec = 0;int ret = select(STDIN_FILENO + 1, &read_fds, NULL, NULL, &timeout);if (ret == -1) {std::cerr << "select failed!" << std::endl;return -1;} else if (ret == 0) {std::cout << "Timeout: No input within 5 seconds" << std::endl;} else {if (FD_ISSET(STDIN_FILENO, &read_fds)) {char buffer[1024];ssize_t len = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);if (len > 0) {buffer[len] = '\0';std::cout << "Input: " << buffer << std::endl;}}}return 0;
}

3.2 异步 I/O 示例(aio_read

这个例子演示了如何使用异步 I/O 读取文件内容。我们将使用 aio_read异步地从文件中读取数据。

#include <iostream>
#include <fstream>
#include <aio.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <errno.h>#define FILE_NAME "test.txt"int main() {// 打开文件int fd = open(FILE_NAME, O_RDONLY);if (fd == -1) {std::cerr << "Failed to open file: " << strerror(errno) << std::endl;return -1;}// 准备异步 I/O 控制块struct aiocb aio;memset(&aio, 0, sizeof(aio));aio.aio_fildes = fd;aio.aio_buf = malloc(1024);  // 分配内存空间用于读取数据aio.aio_nbytes = 1024;aio.aio_offset = 0;// 提交异步读取操作if (aio_read(&aio) == -1) {std::cerr << "aio_read failed: " << strerror(errno) << std::endl;close(fd);return -1;}// 等待异步 I/O 完成while (aio_error(&aio) == EINPROGRESS) {// 在此可以执行其他任务std::cout << "Waiting for I/O to complete..." << std::endl;sleep(1);}// 获取异步 I/O 操作的状态int ret = aio_return(&aio);if (ret == -1) {std::cerr << "aio_return failed: " << strerror(errno) << std::endl;free((void*)aio.aio_buf);close(fd);return -1;}// 输出读取的内容std::cout << "Async read completed, data: " << (char*)aio.aio_buf << std::endl;// 清理资源free((void*)aio.aio_buf);close(fd);return 0;
}

3.3 代码说明

多路复用 I/O (select):

  1. FD_SET()FD_ZERO() 用于设置和清空文件描述符集。
  2. select() 会等待 5 秒钟,如果标准输入文件描述符准备好,可以读取数据。
  3. 如果超时或没有输入,程序会输出“Timeout”。

异步 I/O (aio_read):

  1. 使用 open() 打开一个文件并读取数据。
  2. 设置异步 I/O 控制块 aiocb,并调用 aio_read() 提交异步操作。
  3. 通过 aio_error() 检查 I/O 操作是否正在进行,直到操作完成。
  4. 一旦 I/O 完成,使用 aio_return() 获取结果,并输出异步读取的内容。

编译和运行:

g++ -o async_io async_io.cpp -lrt
./async_io

4. 参考文档

一顿饭的事儿,搞懂了Linux5种IO模型


http://www.ppmy.cn/devtools/133351.html

相关文章

电脑不显示wifi列表怎么办?电脑不显示WiF列表的解决办法

有用户会遇到电脑总是不显示wifi列表的问题&#xff0c;但是不知道要怎么解决。随着无线网络的普及和使用&#xff0c;电脑无法显示WiFi列表的问题有时会让人感到困扰。电脑不显示WiFi列表是很常见的问题&#xff0c;但这并不意味着你无法连接到网络。不用担心&#xff0c;这个…

『Django』APIView基于类的用法

点赞 关注 收藏 学会了 本文简介 上一篇文章介绍了如何使用APIView创建各种请求方法&#xff0c;介绍的是通过函数的方式写接口。 本文要介绍 Django 提供的基于类&#xff08;Class&#xff09;来实现的 APIView 用法&#xff0c;代码写起来更简单。 APIView基于类的基…

【网络安全 | 身份授权】一文讲清OAuth

未经许可,不得转载。 文章目录 问题背景名词定义OAuth设计理念OAuth运行流程OAuth 2.0 客户端的授权模式授权码模式授权码模式的流程流程详细解析简化模式简化模式的流程密码模式客户端模式更新令牌令牌与密码的区别总结问题背景 OAuth 2.0 是一种开放的授权框架,用于在用户…

python在word中插入图片

本文讲解python如何在word文档中插入图片&#xff0c;以及指定插入图片的段落。 1、在新建的word文档中插入图片 import win32com.client as win32 from win32com.client import constants # 1&#xff09;打开word应用程序 doc_app win32.gencache.EnsureDispatch(Word.App…

【人工智能】利用大语言模型(LLM)实现机器学习模型选择与实验的自动化

文章目录 引言环境准备数据集说明 项目结构主要文件说明 导入必要的软件包软件包功能简述 辅助函数定义加载配置文件加载数据集预处理数据集函数功能详解 集成LLM进行模型选择调用LLM的函数定义函数功能详解 清理和验证LLM的输出清理超参数建议提取模型名称验证超参数修正超参数…

Qt_day7_文件IO

目录 文件IO 1. QFileDialog 文件对话框&#xff08;熟悉&#xff09; 2. QFileInfo 文件信息类&#xff08;熟悉&#xff09; 3. QFile 文件读写类&#xff08;掌握&#xff09; 4. UI操作与耗时操作&#xff08;掌握&#xff09; 5. 多线程&#xff08;掌握&#xff09;…

C#界面设计

C#界面设计通常指的是使用C#编程语言及其相关的图形用户界面&#xff08;GUI&#xff09;框架&#xff08;如Windows Forms、WPF&#xff08;Windows Presentation Foundation&#xff09;或Uno Platform等&#xff09;来创建应用程序的用户界面。以下是一些关于C#界面设计的基…

贪心算法day05(k次取反后最大数组和 田径赛马)

目录 1.k次取反后最大化的数组和 2.按身高排序 3.优势洗牌 1.k次取反后最大化的数组和 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 代码&#xff1a; class Solution {public int largestSumAfterKNegations(int[] nums, int k) {//如…