io_submit系统调用及示例

io_submit系统调用及示例

1. 函数介绍

在使用 io_setup 创建了异步 I/O 上下文之后,下一步就是向这个上下文提交实际的 I/O 请求。

io_submit 系统调用的作用就是将一个或多个异步 I/O 请求提交到指定的异步 I/O 上下文中 。每个请求都由一个 struct iocb(I/O Control Block)结构体描述,该结构体包含了操作类型(读/写/同步)、文件描述符、缓冲区地址、读写字节数、文件偏移量等所有必需的信息。

提交后,内核会接管这些请求,并在后台(可能使用专门的线程或机制)执行这些 I/O 操作。调用 io_submit 的进程可以立即继续执行,无需等待 I/O 完成。

简单来说,io_submit 就是把写好的"异步任务清单"(iocb 结构体)交给之前创建的"任务管理器"(io_context_t),让它开始执行这些任务。

2. 函数原型

c 复制代码
// 需要定义宏来启用 AIO 相关定义
#define _GNU_SOURCE
#include <linux/aio_abi.h> // 包含 iocb 等定义
#include <sys/syscall.h>   // 包含系统调用号
#include <unistd.h>        // 包含 syscall 函数

// io_submit 系统调用的实际接口
long syscall(SYS_io_submit, io_context_t ctx_id, long nr, struct iocb **iocbpp);

注意 :这也是一个底层系统调用,通常需要通过 syscall() 函数调用。

3. 功能

nr 个异步 I/O 请求(由 iocbpp 指向的数组描述)提交到由 ctx_id 标识的异步 I/O 上下文中。内核会尝试立即开始处理这些请求。

4. 参数

  • ctx_id :
    • io_context_t 类型。
    • io_setup 返回的、有效的异步 I/O 上下文的标识符。
  • nr :
    • long 类型。
    • 指定要提交的异步 I/O 请求数量。这个值应该与 iocbpp 数组的大小相对应。
  • iocbpp :
    • struct iocb ** 类型。
    • 一个指针数组,数组中的每个元素都指向一个 struct iocb 结构体。struct iocb 描述了一个单独的异步 I/O 请求。
    • 数组的大小至少为 nr

5. struct iocb 结构体 (关键部分)

这是描述单个异步 I/O 请求的核心结构体。

c 复制代码
// 简化版,实际定义在 linux/aio_abi.h
struct iocb {
    __u64 aio_data;          // 用户定义的数据,用于匹配请求和完成事件
    __u32 aio_key, aio_reserved1;
    __u16 aio_lio_opcode;    // 操作类型 (IOCB_CMD_PREAD, IOCB_CMD_PWRITE, ...)
    __s16 aio_reqprio;       // 请求优先级 (通常为 0)
    __u32 aio_fildes;        // 文件描述符
    __u64 aio_buf;           // 用户空间缓冲区地址
    __u64 aio_nbytes;        // 传输字节数
    __s64 aio_offset;        // 文件偏移量
    // ... 其他字段用于高级功能
};

关键字段

  • aio_lio_opcode : 指定操作类型。
    • IOCB_CMD_PREAD: 异步预读(指定偏移量的读取)。
    • IOCB_CMD_PWRITE: 异步预写(指定偏移量的写入)。
    • IOCB_CMD_FSYNC: 异步文件数据和元数据同步。
    • IOCB_CMD_FDSYNC: 异步文件数据同步。
  • aio_fildes: 进行 I/O 操作的目标文件描述符。
  • aio_buf: 用户空间缓冲区的地址(读取时存放数据,写入时提供数据)。
  • aio_nbytes: 要传输(读取或写入)的字节数。
  • aio_offset : 文件中的偏移量(类似 pread/pwrite)。
  • aio_data : 用户自定义数据。当这个请求完成后,对应的完成事件 (io_event) 会包含这个值,方便程序识别是哪个请求完成了。

6. 返回值

  • 成功 : 返回实际成功提交的请求数 (一个非负整数,可能小于或等于 nr)。
  • 失败 : 返回 -1,并设置 errno。如果返回一个 0 到 nr 之间的正数 m,则表示只有数组中前 m 个请求被成功提交,后面的提交失败了。

7. 错误码 (errno)

  • EAGAIN: 资源暂时不可用,例如内核的提交队列已满。
  • EBADF : ctx_id 无效,或者 iocbpp 中某个 iocbaio_fildes 是无效的文件描述符。
  • EINVAL : ctx_id 无效,或者 iocbpp 中某个 iocb 的参数无效(例如 aio_lio_opcode 未知,或 nr 为负数)。
  • ENOMEM: 内存不足。

8. 相似函数或关联函数

  • io_setup : 创建异步 I/O 上下文,是 io_submit 的前置步骤。
  • io_getevents: 用于获取已提交请求的完成状态(事件)。
  • io_cancel: 尝试取消一个已提交但尚未完成的 I/O 请求。
  • struct iocb: 描述单个异步 I/O 请求的结构体。

9. 示例代码

下面的示例演示如何使用 io_setup 创建上下文,然后使用 io_submit 提交异步写入请求。

c 复制代码
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <linux/aio_abi.h>
#include <sys/syscall.h>

// 封装 io_setup 系统调用
static inline int my_io_setup(unsigned nr_events, io_context_t *ctxp) {
    return syscall(__NR_io_setup, nr_events, ctxp);
}

// 封装 io_destroy 系统调用
static inline int my_io_destroy(io_context_t ctx) {
    return syscall(__NR_io_destroy, ctx);
}

// 封装 io_submit 系统调用
static inline int my_io_submit(io_context_t ctx, long nr, struct iocb **iocbpp) {
    return syscall(__NR_io_submit, ctx, nr, iocbpp);
}

// 辅助函数:初始化一个异步写入的 iocb 结构
void prep_pwrite(struct iocb *iocb, int fd, const void *buf, size_t count, __u64 offset) {
    memset(iocb, 0, sizeof(*iocb)); // 清零结构体
    iocb->aio_lio_opcode = IOCB_CMD_PWRITE; // 设置操作类型为异步写
    iocb->aio_fildes = fd;                 // 设置文件描述符
    iocb->aio_buf = (__u64)(unsigned long)buf; // 设置缓冲区地址
    iocb->aio_nbytes = count;              // 设置写入字节数
    iocb->aio_offset = offset;             // 设置文件偏移量
    iocb->aio_data = (__u64)(unsigned long)buf; // 设置用户数据 (这里用 buf 地址)
}

int main() {
    const char *filename = "io_submit_test_file.txt";
    const int num_writes = 3;
    const size_t chunk_size = 1024;
    int fd;
    io_context_t ctx = 0; // 必须初始化为 0
    struct iocb iocbs[num_writes];
    struct iocb *iocb_ptrs[num_writes];
    char buffers[num_writes][chunk_size];
    int ret, i;

    printf("--- Demonstrating io_submit ---\n");

    // 1. 初始化要写入的数据
    for (i = 0; i < num_writes; ++i) {
        memset(buffers[i], 'A' + i, chunk_size); // Fill with 'A', 'B', 'C'
    }

    // 2. 创建并打开文件
    fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0644);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }
    printf("1. Opened/created file '%s' (fd=%d)\n", filename, fd);

    // 3. 初始化异步 I/O 上下文
    ret = my_io_setup(num_writes, &ctx);
    if (ret < 0) {
        perror("io_setup");
        close(fd);
        exit(EXIT_FAILURE);
    }
    printf("2. Initialized AIO context (ctx_id=%llu)\n", (unsigned long long)ctx);

    // 4. 准备 I/O 请求 (iocb)
    printf("3. Preparing %d asynchronous write requests...\n", num_writes);
    for (i = 0; i < num_writes; ++i) {
        prep_pwrite(&iocbs[i], fd, buffers[i], chunk_size, i * chunk_size);
        iocb_ptrs[i] = &iocbs[i];
        printf("   Prepared write %d: offset=%zu, size=%zu, data='%c'...\n",
               i+1, i * chunk_size, chunk_size, 'A' + i);
    }

    // 5. 提交 I/O 请求
    printf("4. Submitting %d write requests using io_submit...\n", num_writes);
    ret = my_io_submit(ctx, num_writes, iocb_ptrs);
    if (ret != num_writes) {
        fprintf(stderr, "   io_submit failed: submitted %d requests, expected %d\n", ret, num_writes);
        if (ret < 0) {
            perror("   io_submit error");
        } else {
            printf("   Only the first %d requests were submitted successfully.\n", ret);
        }
        // 清理并退出
        my_io_destroy(ctx);
        close(fd);
        unlink(filename);
        exit(EXIT_FAILURE);
    }

    printf("   io_submit succeeded. All %d requests submitted.\n", ret);

    // 6. 注意:此时写入操作可能仍在进行中,我们需要用 io_getevents 来等待完成
    // 这个例子只演示提交,不等待完成。
    printf("5. Note: io_submit returned immediately. The writes are happening in the background.\n");
    printf("   To get the results, you need to call io_getevents().\n");

    // 7. 清理资源 (在真实程序中,你应该在 io_getevents 确认完成后再关闭文件)
    printf("6. Cleaning up resources...\n");
    my_io_destroy(ctx);
    printf("   Destroyed AIO context.\n");
    close(fd);
    printf("   Closed file descriptor.\n");
    unlink(filename); // 删除测试文件
    printf("   Deleted test file '%s'.\n", filename);

    printf("\n--- Summary ---\n");
    printf("1. io_submit(ctx_id, nr, iocb_ptrs) submits 'nr' AIO requests to the context 'ctx_id'.\n");
    printf("2. Each request is described by an 'iocb' struct, pointed to by elements in 'iocb_ptrs'.\n");
    printf("3. It returns the number of requests successfully submitted (may be < nr on partial failure).\n");
    printf("4. It returns immediately; the I/O happens asynchronously in the background.\n");
    printf("5. Use io_getevents() afterwards to check for completion and get results.\n");

    return 0;
}

10. 编译和运行

bash 复制代码
# 假设代码保存在 io_submit_example.c 中
gcc -o io_submit_example io_submit_example.c

# 运行程序
./io_submit_example

11. 预期输出

复制代码
--- Demonstrating io_submit ---
1. Opened/created file 'io_submit_test_file.txt' (fd=3)
2. Initialized AIO context (ctx_id=123456789)
3. Preparing 3 asynchronous write requests...
   Prepared write 1: offset=0, size=1024, data='A'...
   Prepared write 2: offset=1024, size=1024, data='B'...
   Prepared write 3: offset=2048, size=1024, data='C'...
4. Submitting 3 write requests using io_submit...
   io_submit succeeded. All 3 requests submitted.
5. Note: io_submit returned immediately. The writes are happening in the background.
   To get the results, you need to call io_getevents().
6. Cleaning up resources...
   Destroyed AIO context.
   Closed file descriptor.
   Deleted test file 'io_submit_test_file.txt'.

--- Summary ---
1. io_submit(ctx_id, nr, iocb_ptrs) submits 'nr' AIO requests to the context 'ctx_id'.
2. Each request is described by an 'iocb' struct, pointed to by elements in 'iocb_ptrs'.
3. It returns the number of requests successfully submitted (may be < nr on partial failure).
4. It returns immediately; the I/O happens asynchronously in the background.
5. Use io_getevents() afterwards to check for completion and get results.

相关推荐
朱包林2 分钟前
数据库服务-日志管理-备份与恢复-主从同步
linux·运维·服务器·数据库·mysql·云计算
李法师_13 分钟前
lwIP MQTT 心跳 Bug 分析与修复
linux·c语言·stm32·单片机·lwip
超级大坏蛋20181 小时前
QT .pro文件的常见用法
java·linux·qt
我好饿13 小时前
Linux入门教程 第十五章 Linux 系统调优工具
linux·运维·网络
萌虎爱分享3 小时前
Linux 防火墙 (firewalld) 管理完整指南
linux·运维·防火墙·firewalld
祈祷苍天赐我java之术8 小时前
Linux 进阶之性能调优,文件管理,网络安全
java·linux·运维
ajassi20009 小时前
开源 C++ QT Widget 开发(七)线程--多线程及通讯
linux·c++·qt·开源
孤雪心殇9 小时前
如何安全,高效,优雅的提升linux的glibc版本
linux·后端·golang·glibc
bdgtd881789 小时前
动态修补C扩展模块的函数指针有哪些风险?安全的修补方案是什么?
c语言·开发语言·安全
潇凝子潇9 小时前
获取服务器指标的信息
linux·运维·服务器