linux AIO

article/2025/9/13 22:11:38

AIO 是 Linux 下的异步读写模型,它是 2.6 内核提供的一个标准增强特性。对于文件的读写,即使以 O_NONBLOCK 方式来打开一个文件,也会处于 “阻塞” 状态,因为文件时时刻刻处于可读状态,而从磁盘到内存所等待的时间是惊人的。

为了充份发挥把数据从磁盘复制到内存的时间,引入了 AIO 模型,其基本原理是允许进程发起很多 I/O 操作,而不用阻塞或等待任何操作完成。稍后或在接收到 I/O 操作完成的通知时,进程就可以检索 I/O 操作的结果。

IO 模型

下图给出了同步和异步,以及阻塞和非阻塞的模型。简单来说,一个系统调用 (read, select) 立即返回表示非阻塞;在一个时间点只能去处理一个请求表示同步。

每个 I/O 模型都有自己的使用模式,它们对于特定的应用程序都有自己的优点。

同步阻塞IO

最常用的模型,当用户空间的应用执行一个系统调用后,会导致应用程序阻塞,直到系统调用完成为止(数据传输完成或发生错误)。应用程序只能处于一种不再消费 CPU 而只是简单等待响应的状态,因此从处理的角度来看,这是非常有效的。

 

在调用 read 系统调用时,应用程序会阻塞并对内核进行上下文切换;然后会触发读磁盘操作,当从读取的设备中返回后,数据就被移动到用户空间的缓冲区中;然后应用程序就会解除阻塞(read 调用返回)。

同步非阻塞IO

备以非阻塞的形式打开,这意味着 I/O 操作不会立即完成,read 操作可能会返回一个错误代码,说明这个命令不能立即满足(EAGAIN 或 EWOULDBLOCK)。

该模型中可能需要应用程序调用多次来等待操作完成,这样的效率仍然不高,因为很多情况下,当内核执行这个命令时,应用程序必须要进行忙碌等待,直到数据可用为止,或者试图执行其他工作。

正如上图所示,这个方法会引入 I/O 操作的延时,因为数据在内核中变为可用到用户调用 read 返回数据之间存在一定的间隔,这会导致整体数据吞吐量的降低。

异步阻塞IO

阻塞通知的非阻塞 I/O,配置的是非阻塞 I/O,然后使用阻塞 select 系统调用来确定一个 I/O 描述符何时有操作。select 可以为多个描述符提供通知,而不是仅仅为一个描述符提供通知;通知的事件包括写数据、有读数据可用以及是否发生错误。

 

select 调用的主要问题是它的效率不是非常高,尽管这是异步通知使用的一种方便模型,但是对于高性能的 I/O 操作来说不建议使用。

异步非阻塞IO(AIO)

一种处理与 I/O 重叠进行的模型,读请求会立即返回,说明 read 请求已经成功发起了;然后应用程序会执行其他处理操作;当 read 的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次 I/O 处理过程。

该模型可以重叠执行多个 I/O 请求以及 CPU 操作,利用了处理速度与 I/O 速度之间的差异。当一个或多个 I/O 请求挂起时,此时 CPU 可以执行其他任务;或者更为常见的是,在发起其他 I/O 的同时对已经完成的 I/O 进行操作。

Linux AIO简介

Linux 中有两套异步 IO,一套是由 glibc 实现的 aio_* 系列,通过线程+阻塞调用在用户空间模拟 AIO 的功能,不需要内核的支持,类似的还有 libeio;另一套是采用原生的 Linux AIO,并由 libaio 来封装调用接口,相比来说更底层。

glibc 的实现性能比较差,在此先介绍 libaio 的使用。libaio 的使用并不复杂,过程为:A) libaio 的初始化; B) IO 请求的下发和回收,C) libaio 销毁。提供了下面五个主要 API 函数以及宏定义:

int io_setup(int maxevents, io_context_t *ctxp);
int io_destroy(io_context_t ctx);
int io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);void io_set_callback(struct iocb *iocb, io_callback_t cb);
void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset);
void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset);
void io_prep_pwritev(struct iocb *iocb, int fd, const struct iovec *iov, int iovcnt, long long offset);
void io_prep_preadv(struct iocb *iocb, int fd, const struct iovec *iov, int iovcnt, long long offset);struct iocb {PADDEDptr(void *data, __pad1);  /* Return in the io completion event */PADDED(unsigned key, __pad2);   /* For use in identifying io requests */short       aio_lio_opcode;short       aio_reqprio;int         aio_fildes;union {struct io_iocb_common    c;struct io_iocb_vector    v;struct io_iocb_poll      poll;struct io_iocb_sockaddr  saddr;} u;
};

后五个宏定义是为了操作 struct iocb 结构体,该结构体是 libaio 中很重要的一个结构体,用于表示 IO,但是其结构略显复杂,为了保持封装性不建议直接操作其元素而用上面五个宏定义操作。

初始化

用来初始化类型为 io_context_t 的变量,这个变量为 libaio 的工作空间,可以采用 io_setup() 或者 io_queue_init(),两者功能实际相同。

自定义字段

这一阶段是可选的,在 struct iocb 中保留了供用户自定义的元素,也就是 void *data,可以通过 io_set_callback() 设置回调函数,或者通过 iocbp->data=XXX 自定义。

需要注意的是,两者均使用 data 变量,因此不能同时使用。

读写请求下发

读写均通过 io_submit() 下发,之前需要通过 io_prep_pwrite()io_prep_pread() 生成 struct iocb 做为该函数的参数。在这个结构体中指定了读写类型、起始扇区、长度和设备标志符等信息。

读写请求回收

使用 io_getevents() 等待 IO 的结束信号,该函数返回 events[] 数组,nr 为数组的最大长度,min_nr 为最少返回的 events 数,timeout 可填 NULL 表示一直等待。

struct io_event {PADDEDptr(void *data, __pad1);PADDEDptr(struct iocb *obj,  __pad2);PADDEDul(res,  __pad3);PADDEDul(res2, __pad4);
};

其中,res 为实际完成的字节数;res2 为读写成功状态,0 表示成功;obj 为之前下发的 struct iocb 结构体。

销毁

直接通过 io_destory() 销毁即可。

示例

下面是一个简单的示例,通过 AIO 写入到 foobar.txt 文件中。

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <libaio.h>int main(void)
{int               output_fd;struct iocb       io, *p=&io;struct io_event   e;struct timespec   timeout;io_context_t      ctx;const char        *content="hello world!";// 1. init the io context.memset(&ctx, 0, sizeof(ctx));if(io_setup(10, &ctx)){printf("io_setup error\n");return -1;}// 2. try to open a file.if((output_fd=open("foobar.txt", O_CREAT|O_WRONLY, 0644)) < 0) {perror("open error");io_destroy(ctx);return -1;}// 3. prepare the data.io_prep_pwrite(&io, output_fd, (void*)content, strlen(content), 0);//io.data = content;   // set or notif(io_submit(ctx, 1, &p) < 0){io_destroy(ctx);printf("io_submit error\n");return -1;}// 4. wait IO finish.while(1) {timeout.tv_sec  = 0;timeout.tv_nsec = 500000000; // 0.5sif(io_getevents(ctx, 0, 1, &e, &timeout) == 1) {close(output_fd);break;}printf("haven't done\n");sleep(1);}io_destroy(ctx);return 0;
}

然后,可以通过 gcc foobar.c -o foobar -laio -Wall 进行编译。

POSIX AIO

也就是 glibc 中包含的版本,主要包含如下接口:

#include <aio.h>// 提交一个异步读/写,通过结构体告知系统读取的文件、起始位置、读取字节数、读取后的写入buffer
int aio_read(struct aiocb *aiocbp);
int aio_write(struct aiocb *aiocbp);// 检查当前AIO的状态,可用于查看请求是否成功,返回0(成功)EINPROGRESS(正在读取)
int aio_error(const struct aiocb *aiocbp);// 查看一个异步请求的返回值,如果成功则返回读取字节数,否则返回-1,此时跟同步读写定义的一样
ssize_t aio_return(struct aiocb *aiocbp);         

示例程序

如下是一个测试示例。

#include <aio.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>// dd if=/dev/urandom of="foobar.txt" count=10000
const int SIZE_TO_READ = 100;int main()
{int file = open("foobar.txt", O_RDONLY, 0);if (file == -1) {printf("Unable to open file!\n");exit(EXIT_FAILURE);}char* buffer = (char *)malloc(SIZE_TO_READ);struct aiocb cb;memset(&cb, 0, sizeof(struct aiocb));cb.aio_nbytes = SIZE_TO_READ;cb.aio_fildes = file;cb.aio_offset = 0;cb.aio_buf = buffer;if (aio_read(&cb) == -1) {printf("Unable to create request!\n");close(file);exit(EXIT_FAILURE);}printf("Request enqueued!\n");// wait until the request has finishedwhile(aio_error(&cb) == EINPROGRESS) {printf("Working...\n");}int numBytes = aio_return(&cb); // success?if (numBytes != -1)printf("Success!\n");elseprintf("Error!\n");free(buffer);close(file);return 0;
}

Linux direct-io简介

在 Linux 2.6 使用 direct io 需要按照如下几点来做:

  1. 在源文件的最顶端加上 #define _GNU_SOURCE 宏定义,或在编译时在命令行上指定,否则编译报错。
  2. 在 open() 时加上 O_DIRECT 标志。
  3. 存放文件数据的缓存起始位置以及每次读写数据长度必须是磁盘逻辑块大小的整数倍,一般是 512 字节,否则将导致读写失败,返回 -EINVAL。

对于第 3 点,要满足缓存区起始位置对齐,可以在进行缓存区空间申请时使用 posix_memalign 这样的函数指定字节对齐。

ret = posix_memalign((void **)&buf, 512, BUF_SIZE);
real_buf = malloc(BUF_SIZE + 512);
aligned_buf = ((((unsigned int)real_buf + 512 - 1) / 512) * 512);

由于要满足每一次读写数据长度必须是磁盘逻辑块大小的整数倍,所以最后一次文件操作可能无法满足,此时只能重新以cached io模式打开文件后,fseek到对应位置进行剩余数据的读写。

参考

使用异步 I/O 大大提高应用程序的性能 介绍各种 IO 模型不错的参考;在内核文档 linux-io.txt 中介绍了 Linux AIO 机制以及部分 libaio、librt 的内容。

linux aio 内核实现 - 知乎


http://chatgpt.dhexx.cn/article/i3OcG3jO.shtml

相关文章

AIO模型

目录 AIO模型介绍 AsynchronousServerSocketChannel&#xff1a;AIO中网络通信服务端的socket 1、future方法 2、callback回调方式 AIO 的回调方式编程 BIO、NIO、AIO的比较 1、释义 BIO&#xff1a;同步阻塞IO模型 NIO&#xff1a;同步非阻塞IO模型 AIO&#xff1a;…

java中IO模型-AIO模型

AIO模型介绍 AIO&#xff08;Asynchronous I/O&#xff09; 异步非阻塞模型&#xff0c; 在javajdk.17版本开始支持AIO&#xff0c;AIO模型需要操作系统的支持。 AIO最大的特性是异步能力&#xff0c;对socket和I/O起作用。 异步IO模型类似的 与NIO模型不同&#xff0c;读写操…

架构解密从分布式到微服务:深入理解网络,AIO

AIO AIO是I/O模型里一个很高的层次&#xff0c;体现了大道至简的软件美学理念。与NIO相比&#xff0c;AIO的框架和使用方法相对简单很多。 AIO包括两大部分:AIO Files解决了文件的异步处理问题&#xff0c;AIO Sockets解决了Socket的异步处理问题。AIO的核心概念为应用发起非…

BIO,NIO,AIO区别

BIO,NIO,AIO 总结 Java 中的 BIO、NIO和 AIO 理解为是 Java 语言对操作系统的各种 IO 模型的封装。程序员在使用这些 API 的时候&#xff0c;不需要关心操作系统层面的知识&#xff0c;也不需要根据不同操作系统编写不同的代码。只需要使用Java的API就可以了。 在讲 BIO,NIO,…

Apache CXF - 快速指南

Apache CXF - 简介 在当今的环境中&#xff0c;您可以使用多个选项来创建 Web 服务应用程序。您可以使用多种标准和广泛接受的协议中的一种或多种进行通信。例如SOAP、XML/HTTP、RESTful HTTP和CORBA&#xff08;通用对象请求代理架构&#xff0c;在过去非常流行&#xff0c;但…

java cxf 安全_CXF client在并发下的线程安全问题

这个是发生在上周周末的真实案例&#xff0c;因为cxf client 端线程安全导致的错误&#xff0c;总结出来希望其他使用cxf的兄弟注意。 首先描述一下背景&#xff0c;简单的说就是使用cxf作为web service的客户端&#xff0c;运行在weblogic上&#xff0c;连接外部的服务器。为了…

linux cxf服务端,Apache CXF 框架应用实战

一、概述 Apache CXF提供了用于方便地构建和开发WebService的可靠基础架构。它允许创建高性能和可扩展的服务&#xff0c;可以部署在Tomcat和基于Spring的轻量级容器中&#xff0c;也可以部署在更高级的服务器上&#xff0c;例如Jboss、WebSphere或WebLogic。 CXF提供了以下功能…

使用CXF调用WSDL

简介 时隔多年&#xff0c;再次遇到需要调用WebService的业务&#xff0c;对方给予的wsdl说明文档还是内网的链接&#xff0c;并且设有基础访问权限&#xff0c;即在浏览器打开wsdl链接时需要输入【用户名密码】登录后方可查看wsdl文档&#xff0c;这需要设置代理&#xff08;我…

spring5.x cxf3.4.x 服务端和客户端 非maven版本

文章目录 一、资料准备1. 官网链接2. 解压3. 依赖梳理 二、spring集成cxf2.1.创建spring项目2.2. 创建接口2.3. impl2.4. spring-cxf.xml2.5. 客户端2.6. 开源项目 一、资料准备 1. 官网链接 http://cxf.apache.org/download.html 下载apache-cxf-3.4.5.zip 2. 解压 3. 依赖…

CXF实现WebService

一、CXF简介 Apache CXF Celtix XFire&#xff0c;开始叫 Apache CeltiXfire&#xff0c;后来更名为 Apache CXF 了&#xff0c;以下简称为 CXF。CXF 继承了 Celtix 和 XFire 两大开源项目的精华&#xff0c;提供了对 JAX-WS 全面的支持&#xff0c;并且提供了多种 Binding …

SpringBoot2 整合 CXF 服务端和客户端

文章目录 一、CXF服务端1. 导入依赖2. 创建service接口3. 接口实现类4. cxf配置类5. 查看wsdl结果 二、CXF客户端2.1. 客户端2.2. 断点调试2.3. 发起调用服务开源源码. 一、CXF服务端 1. 导入依赖 <properties><cxf.version>3.3.1</cxf.version></proper…

CXF客户端乱码

CXF客户端乱码 解决办法一&#xff0c;设置服务端代码&#xff1a; 在使用CXF与其他系统对接时&#xff0c;发现对方系统响应的汉字乱码&#xff0c;使用soapui调用测试就没有问题&#xff0c;但是程序里面调用就乱码&#xff0c;很奇怪&#xff0c;乱码如下&#xff1a; 由…

SpringBoot集成CXF

CXF入门篇https://blog.csdn.net/tongxin_tongmeng/article/details/126482362Server端项目结构 Server端pom.xml <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"…

走进cxf

一、什么是cxf 有很多人认为cxf就是webservice&#xff0c;其实cxf只是发布调用webservice的工具而已 Apache CXF Celtix Xfire&#xff0c;开始叫 Apache CeltiXfire&#xff0c;后来更名为 Apache CXF 了&#xff0c;以下简称为 CXF。Apache CXF 是一个开源的 web Service…

NewSQL ---- Mysql.8.0 与 MemSQL 7.0 大数据量查询性能对比

目录 1测试环境以及测试用例设计 1.1测试环境 1.2测试用例设计 2 千万级数据量性能测试对比 2.1 MemSQL时间范围分页查询 2.1.1 性能测试数据 2.2任务信息查询 2.2.1 性能测试数据 2.3 执行批次范围查询 2.3.1 性能测试数据 2.4 批次任务查询 2.4.1 性能测试数据 …

memsql架构2

接上次的MemSQL分布式架构介绍(一)&#xff0c;原文在这里&#xff1a;http://docs.memsql.com/latest/concepts/distributed_architecture/ 首先上张图&#xff0c;是我根据自己的理解画的&#xff0c;如有错误还请大家指出 几个概念 1、MemSQL有两种类型的表&#xff1a; ref…

MemSQL性能测试结果

1.查询的SQL select count(subie.user_id) as count from sum_user_basic_info_exp subie join sum_user_lend_info_exp sulie on sulie.user_idsubie.user_id where subie.curr_user_role_cd1 and subie.reg_dt >2016-08-29 and subie.reg_dt <2016-08-29 结…

【MySQL】SQL优化

SQL优化 1 插入数据 1.1 insert优化 如果我们需要一次性往数据库表中插入多条记录&#xff0c;可以从以下三个方面进行优化。 insert into tb_test values(1,tom); insert into tb_test values(2,cat); insert into tb_test values(3,jerry); .....1.批量插入数据 Insert…

MySQL慢SQL探究

文章目录 前言1、慢SQL捕获慢查询追踪配置方式 2、情况分析为什么查询会慢&#xff1f; 2.1 SQL执行计划分析explain执行计划分析PROFILE分析OPTIMIZER_TRACE分析 3、引擎参数配置分析I/O性能分析MySQL I/O参数 其他原因分析网络抖动单表数据量过大 总结 前言 我们在日常开发中…

【Mysql】SQL性能分析

【Mysql】SQL性能分析 文章目录 【Mysql】SQL性能分析1. SQL执行频率2. 慢查询日志3. profile详情4. explain 1. SQL执行频率 在控制台中通过命令 show [session|global] status 命令可以提供服务器状态信息。通过如下指令&#xff0c;可以查看当前数据库的 insert,update,del…