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

VxWorks入门小白菜鸟教程4 —— 异步I/O库(AIO)的使用

一、功能描述

本库根据 POSIX 1003.1b 标准实现异步 I/OAIO)。AIO 的核心价值是让应用程序的计算任务与 I/O 操作并行执行,具体特性包括:

  1. 支持对单个文件同时发起多次 I/O 操作。
  2. 支持同时对多个文件发起 I/O 操作。
  3. 异步 I/O 发起后,会与应用程序逻辑并行执行,效果等同于由独立线程处理 I/O 任务。

二、头文件

使用 AIO 功能时,需包含以下头文件:

#include <aio.h>

三、核心函数(ROUTINES

以下函数用于发起、管理和查询异步 I/O 操作,均符合 POSIX 标准:

  1. aio_read() - 发起异步读操作
  2. aio_write() - 发起异步写操作
  3. lio_listio() - 发起一组异步 I/O 请求
  4. aio_suspend() - 等待异步 I/O 请求完成
  5. aio_cancel() - 取消已提交的异步 I/O 请求
  6. aio_fsync() - 异步文件同步
  7. aio_error() - 获取异步 I/O 操作的错误状态
  8. aio_return() - 获取异步 I/O 操作的返回状态

四、AIO 环境变量

加载实时进程(RTP)时,可通过以下环境变量配置 AIO 库的运行参数。默认值由系统头文件定义。

环境变量

说明

默认设置(由头文件定义)

MAX_LIO_CALLS

最大未完成的 lio_listio() 调用数量

AIO_CLUST_MAX

MAX_AIO_SYS_TASKS

支持 AIO 的系统任务(线程)数量

AIO_IO_TASKS_DFLT

AIO_TASK_PRIORITY

AIO 系统任务的优先级

AIO_IO_PRIO_DFLT

AIO_TASK_STACK_SIZE

AIO 系统任务的栈大小

AIO_IO_STACK_DFLT

五、AIO 操作流程

异步 I/O 的操作流程需遵循以下规则,核心是通过标准文件描述符管理 I/O,并通过专用函数查询操作结果:

  1. 文件打开:需通过标准 open() 函数打开文件,获取的文件描述符将用于后续所有 AIO 调用。
  2. 发起 I/O 请求:通过 aio_read()aio_write() lio_listio() 发起异步操作。这些函数的返回值仅表示请求是否成功提交到队列,不代表 I/O 操作本身的成功或失败。
  3. 查询操作结果:
  1. aio_error():获取 I/O 操作的错误状态。操作未完成时,返回值固定为 EINPROGRESS
  2. aio_return():操作完成后,通过此函数获取最终返回状态(如实际读写的字节数)。
  1. 其他管理操作:
  1. aio_cancel():取消已提交但未完成的 I/O 请求。
  2. aio_suspend():阻塞等待指定的 I/O 请求完成。
  3. aioShow():非 POSIX 标准函数,用于显示当前未完成的 AIO 请求(Wind River 扩展)。

六、异步 I/O 控制块

所有 AIO 函数均需传入 AIO 控制块(aiocb 作为参数,用于描述单个异步 I/O 操作的详细信息。使用时需严格遵守以下规则:

aiocb 的使用约束:

  1. 内存分配:调用者需自行分配 aiocb 内存,且在 I/O 操作完成并调用 aio_return() 前,该内存不可释放。
  2. 禁止在任务栈上创建 aiocb(除非调用者会阻塞到 I/O 完成并执行 aio_return()),否则栈内存可能被覆盖。
  3. 不可重复使用:同一 aiocb 不能同时用于多个异步操作,否则会导致未定义行为。
  4. 提交后不可修改:aiocb 提交给系统后(如调用 aio_write() 后),需等待 aio_return() 执行完成,才能修改、释放或复用该 aiocb

aiocb 结构体在 aio.h 中定义,包含以下成员(部分为 Wind River 扩展):

struct aiocb
{int                 aio_fildes;      // 文件描述符off_t               aio_offset;      // 文件偏移量volatile void *     aio_buf;         // I/O 数据缓冲区地址size_t              aio_nbytes;      // 读写字节数int                 aio_reqprio;     // 请求优先级调整值struct sigevent     aio_sigevent;    // 完成通知信号(可选)int                 aio_lio_opcode;  // lio_listio() 操作类型AIO_SYS             aio_sys;         // 系统内部使用(用户不可修改)
};

结构体成员说明:

  1. aio_fildes:用于 I/O 操作的文件描述符(由 open() 函数返回)。
  2. aio_offset:文件读写的起始偏移量(从文件开头计算)。
  3. 注意:AIO 不会自动更新文件偏移量(与 read()/write() 不同),每次操作需手动设置正确的 aio_offset
  4. aio_buf:指向 I/O 数据缓冲区的指针(读操作时为数据接收缓冲区,写操作时为数据发送缓冲区)。
  5. aio_nbytes:请求读写的字节数。
  6. aio_reqprio:降低 AIO 请求优先级的数值(仅能降低,不能提高)。
  1. 取值范围:0 AIO_PRIO_DELTA_MAX
  2. 若调整后优先级低于系统最低值,则自动使用最低优先级。
  1. aio_sigevent:可选配置,用于定义 I/O 完成时发送的信号(如 SIGEV_SIGNAL 表示发送指定信号)。
  2. aio_lio_opcode:仅用于 lio_listio() 函数,指定操作类型,可选值为 LIO_READ(读)、LIO_WRITE(写)、LIO_NOP(无操作)。
  3. aio_sysWind River 扩展成员,系统内部使用,用户禁止修改。

七、参考示例

以下示例展示了基于 AIO 的异步写和异步读实现,包含信号通知机制的使用。

示例 1:异步写操作

struct aiocb *pAioWrite;
int fd; // 假设已通过 open() 获取文件描述符
char *buffer = malloc(BUF_SIZE); // 数据缓冲区
// 1. 分配并初始化 aiocb
if ((pAioWrite = calloc(1, sizeof(struct aiocb))) == NULL)
{printf("calloc 分配 aiocb 失败\n");return ERROR;
}
pAioWrite->aio_fildes = fd;          // 绑定文件描述符
pAioWrite->aio_buf = buffer;         // 绑定数据缓冲区
pAioWrite->aio_offset = 0;           // 从文件开头写入
strcpy(pAioWrite->aio_buf, "test string"); // 写入数据
pAioWrite->aio_nbytes = strlen("test string"); // 写入字节数
pAioWrite->aio_sigevent.sigev_notify = SIGEV_NONE; // 不使用信号通知
// 2. 发起异步写请求
aio_write(pAioWrite);
// 3. 并行执行其他任务(此处省略)
// ... 其他业务逻辑 ...
// 4. 等待写操作完成
while (aio_error(pAioWrite) == EINPROGRESS)taskDelay(1); // 延迟 1 个时间片,避免忙等
// 5. 获取结果并释放资源
aio_return(pAioWrite);
free(pAioWrite);
free(buffer);

示例 2:带信号通知的异步读操作

struct aiocb *pAioRead;
int fd; // 假设已通过 open() 获取文件描述符
char *buffer = malloc(BUF_SIZE); // 数据缓冲区
struct sigaction action1;
// 1. 初始化信号处理函数(I/O 完成时触发)
action1.sa_sigaction = sigHandler; // 信号处理函数
action1.sa_flags = SA_SIGINFO;    // 支持扩展信号信息
sigemptyset(&action1.sa_mask);     // 清空信号掩码
sigaction(TEST_RT_SIG1, &action1, NULL); // 注册信号 TEST_RT_SIG1
// 2. 分配并初始化 aiocb
if ((pAioRead = calloc(1, sizeof(struct aiocb))) == NULL)
{printf("calloc 分配 aiocb 失败\n");return ERROR;
}
pAioRead->aio_fildes = fd;                  // 绑定文件描述符
pAioRead->aio_buf = buffer;                 // 绑定接收缓冲区
pAioRead->aio_nbytes = BUF_SIZE;            // 读取字节数
pAioRead->aio_sigevent.sigev_signo = TEST_RT_SIG1; // 完成时发送 TEST_RT_SIG1
pAioRead->aio_sigevent.sigev_notify = SIGEV_SIGNAL; // 通知方式:信号
pAioRead->aio_sigevent.sigev_value.sival_ptr = (void *)pAioRead; // 传递 aiocb 指针
// 3. 发起异步读请求
aio_read(pAioRead);
// 4. 并行执行其他任务(此处省略)
// ... 其他业务逻辑 ...

示例 3:信号处理函数(I/O 完成回调)

void sigHandler(int sig, struct siginfo info, void *pContext)
{struct aiocb *pAioDone;// 从信号信息中获取完成的 aiocbpAioDone = (struct aiocb *)info.si_value.sival_ptr;// 获取 I/O 结果并释放资源aio_return(pAioDone);free(pAioDone);free(pAioDone->aio_buf); // 释放数据缓冲区
}

八、配置说明

AIO 库在使用动态链接(共享库)时需单独配置,静态链接时无需额外操作:

  1. 动态链接:在链接命令中添加 -lAioPx,或在构建环境的 LIBS 宏、Workbench 项目的选项卡中添加该库。
  2. 静态链接:AIO 库已包含在默认链接中,无需额外配置。

九、实操演示

  1. 新建文件夹aio1,在VScode中打开该文件夹,新建3个文件:main.cfailtest.cCMakeLists.txt
  2. main.c中输入代码:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <aio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>#define BUFFER_SIZE 1024  // 读取缓冲区大小
#define FILENAME "test.txt"  // 要读取的文件int main(int argc, char * argv[]) {/* Wait a little (5 seconds) for WRASP I/O to set up after initial boot *//*sleep(5);*/
#ifdef _WRS_KERNELprintf("\nHello world from kernel space!\n");printf("argc=%d\n", argc);printf("argv[%d]=%s\n", argc, argv==NULL ? "NULL" : (char*)argv);
#elseint i;printf("\nHello world from user space!\n");for(i=0; i<argc; i++) {printf("argv[%d]=%s\n", i, argv[i]);}
#endifint fd;struct aiocb aio;  // AIO 控制块ssize_t bytes_read;int ret;// 1. 打开文件(只读模式)fd = open(FILENAME, O_RDONLY);if (fd == -1) {perror("open 失败");  // 打印错误信息(如文件不存在)exit(EXIT_FAILURE);}// 2. 分配读取缓冲区(需保证在AIO操作期间有效)char *buffer = malloc(BUFFER_SIZE);if (buffer == NULL) {perror("malloc 缓冲区失败");close(fd);exit(EXIT_FAILURE);}// 3. 初始化 AIO 控制块(aiocb)memset(&aio, 0, sizeof(struct aiocb));  // 清零结构体,避免未定义行为aio.aio_fildes = fd;                    // 绑定文件描述符aio.aio_buf = buffer;                   // 绑定读取缓冲区aio.aio_nbytes = BUFFER_SIZE;           // 计划读取的字节数aio.aio_offset = 0;                     // 从文件起始位置开始读取// 不使用信号通知(通过轮询等待完成)aio.aio_sigevent.sigev_notify = SIGEV_NONE;// 4. 发起异步读操作ret = aio_read(&aio);if (ret == -1) {perror("aio_read 发起失败");  // 失败原因:如参数错误、文件不可读等free(buffer);close(fd);exit(EXIT_FAILURE);}printf("异步读请求已提交,等待完成...\n");// 5. 轮询等待异步操作完成(实际应用中可并行处理其他任务)while (aio_error(&aio) == EINPROGRESS) {// 这里可以执行其他业务逻辑,而非空等printf("等待中...(可执行其他操作)\n");sleep(1);  // 休眠1秒,减少CPU占用}// 6. 获取异步操作结果bytes_read = aio_return(&aio);if (bytes_read == -1) {perror("异步读操作失败");free(buffer);close(fd);exit(EXIT_FAILURE);}// 7. 处理读取到的数据printf("\n读取完成,共读取 %zd 字节:\n", bytes_read);printf("----------------------------------------\n");fwrite(buffer, 1, bytes_read, stdout);  // 输出读取到的内容printf("\n----------------------------------------\n");// 8. 清理资源free(buffer);close(fd);printf("\n资源已释放,程序退出\n");return 0;
}

        3、在failtest.c中输入代码:

int main() {return 1;  // 故意返回失败
}

        4、在CMakeLists.txt中输入:

cmake_minimum_required(VERSION 2.8.7)#指定项目名称和支持的编程语言:C、C++、汇编
project(aio_test C CXX ASM)#创建一个名为 hello_cmake 的可执行程序,并指定其构建所需的源代码文件。
add_executable(aio_testmain.c
)# 可执行程序链接 VxWorks 的 AIO 库(库名:AioPx)
target_link_libraries(aio_test PRIVATE AioPx)

        5、在VScode中打开终端,执行命令(新建build文件夹,并进入该文件夹):

mkdir build
cd build

        6、在VScode的终端中执行命令(生成构建文件Makefile):

cmake .. -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE="D:\!VxWorks\wrsdk-vxworks7-win-qemu-1.10\wrsdk-vxworks7-win-qemu\vxsdk\sysroot\mk\rtp.toolchain.cmake"

        7、在VScode的终端中执行命令(根据Makefile进行编译链接,生成可执行文件):

make

        8、在build文件夹中新建test.txt,并输入(任意内容都行):

你好,我是aio

        9、在build文件夹中右键->在终端中打开,执行命令(启动ftp服务器,参考小白入门1):

python -m pyftpdlib -p 21 -u target -P vxTarget -i 127.0.0.1

        10、启动VxWorks(参考小白入门1),然后执行aio_test

        完美!

http://www.dtcms.com/a/545045.html

相关文章:

  • 基于k8s部署wordpress博客系统并实现数据的持久化
  • SQL 查询语句的子句的执行顺序
  • sql 双游标循环
  • SQL 189 统计有未完成状态的试卷的未完成数和未完成率
  • 有哪些做微信小游戏的网站建设网站后期人员薪酬
  • 黑龙江交通基础设施建设网站怎么做推广网站
  • 企业微信AI SCRM推荐:从技术适配与场景功能实践进行评估
  • 从开源到落地:SimpleBGC 三轴稳像平台全栈技术解析(下)
  • 零基础新手小白快速了解掌握服务集群与自动化运维(十六)集群部署模块——Keepalived双机热备
  • 网站建设论坛做一个网站的流程
  • 金仓数据库平替MongoDB:银行存款系统国产化实践
  • 基于 Spring AI Alibaba 搭建 Text-To-SQL 智能系统(前置介绍)
  • 搞笑资讯网站源码数据库支持的网站怎么做
  • 友思特应用 | 基于高精度双目散斑 3D 相机的放射治疗视觉定位应用
  • MCU寄存器配置深度解析:从原理到实践
  • 论文学习_LLM4Decompile: Decompiling Binary Code with Large Language Models
  • 【日记】好耶!是新电脑!(3959 字)
  • Day73 嵌入式传感器技术全栈开发
  • 学历提升有几种方式长沙正规seo优化公司
  • 合肥网络公司 网站建设网站建设 预算
  • 23大数据 数据挖掘集合
  • Docker Compose曝路径遍历漏洞,可致任意覆写文件(CVE-2025-62725)
  • 网站可以分为哪些类型怎样优化自己的网站
  • Rust 模式匹配的穷尽性检查:从编译器证明到工程演进
  • C# Entity Framework Core 中的 Include 和 ThenInclude 详解
  • Linux如何远程控制Windows?rdesktop让跨系统操作像本地一样流畅
  • Spring Boot3零基础教程,JVM 编译原理,笔记87
  • Rust 变量声明与可变性:从设计哲学到工程实践
  • 深圳苍松大厦 网站建设对网站做综合搜索引擎优化分析
  • 数据结构 09 二叉树作业