LinuxKernel - io_setup, io_destroy, io_submit, io_cancel, io_getevents解析 (aio.c解析)

linux内核的aio.c文件里面有几个系统调用,分别是io_setup, io_destroy, io_submit, io_cancel, io_getevents

io_submit

系统调用原型

#include <linux/aio_abi.h> 
#include <sys/syscall.h> 
#include <unistd.h> 

long io_submit(aio_context_t ctx_id, long nr, struct iocb **iocbpp);
  • 参数说明:

    • ctx_id:由 io_setup 创建的 AIO 上下文标识符。

    • nr:要提交的异步 I/O 请求数量。

    • iocbpp:指向 iocb 指针数组的指针,每个 iocb 描述一个 I/O 请求。

  • 返回值:

    • 成功时返回实际提交的 I/O 请求数。

    • 失败时返回负值,表示错误码。

  • 错误:

    • EAGAIN:nr_events超出了可用限制事件,如/proc/sys/fs/aio-max-nr中所定义

    • EINVAL:ctx_idp未初始化,或者指定的nr_events超出内部限制。nr_events 应该大于0。

    • ENOMEM:可用的内核资源不足。

    • ENOSYS:io_setup() 未在此架构上实现。

源码解析

static long do_io_submit(aio_context_t ctx_id, long nr, // 这个nr就是有几个事件的意思
			  struct iocb __user *__user *iocbpp, bool compat)
{
	struct kioctx *ctx;
	long ret = 0;
	int i = 0;

	if (unlikely(nr < 0))
		return -EINVAL;

	if (unlikely(nr > LONG_MAX/sizeof(*iocbpp)))
		nr = LONG_MAX/sizeof(*iocbpp);

	if (unlikely(!access_ok(VERIFY_READ, iocbpp, (nr*sizeof(*iocbpp)))))
		return -EFAULT;

	ctx = lookup_ioctx(ctx_id);
	if (unlikely(!ctx)) {
		pr_debug("EINVAL: invalid context id\n");
		return -EINVAL;
	}


	/*
	 * AKPM: should this return a partial result if some of the IOs were
	 * successfully submitted?
	 */
	for (i=0; i<nr; i++) {
		struct iocb __user *user_iocb;
		struct iocb tmp;

		if (unlikely(__get_user(user_iocb, iocbpp + i))) {
			ret = -EFAULT;
			break;
		}

		if (unlikely(copy_from_user(&tmp, user_iocb, sizeof(tmp)))) {
			ret = -EFAULT;
			break;
		}

		ret = io_submit_one(ctx, user_iocb, &tmp, compat); // 很抽象!只要有一个事件失败了,就会直接退出,你还不知道哪些事件完成了!
        // io_getevents?可以获取哪些事件完成了?
		if (ret)
			break;
	}

	percpu_ref_put(&ctx->users);
	return i ? i : ret;
}

64位操作系统可以使用io_getevents去获取哪些事件已完成!io_getevents 返回的事件顺序可能与提交请求的顺序不同,因此需要通过 iocb.dataio_event.obj 来识别每个事件。

PS:这种狗艹代码有一种屎山的美!

io_destroy

#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>

long io_destroy(aio_context_t ctx);
  • 参数说明:

    • ctx: 由 io_setup 创建的 AIO 上下文标识符。

  • 返回值:

    • 成功返回 0

    • 失败返回负值,表示错误码。

io_setup

#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>

long io_setup(unsigned nr_events, aio_context_t *ctx_idp);
  • 参数说明:

    • nr_events:上下文中允许的最大并发 I/O 请求数。

    • ctx_idp:指向 aio_context_t 的指针,用于返回创建的 AIO 上下文标识符。

  • 返回值:

    • 成功时返回 0

    • 失败时返回负值,表示错误码。

io_cancel

io_cancel 是 Linux 内核中的系统调用,用于尝试取消一个异步 I/O 请求。它通常用于在已经提交异步 I/O 请求的情况下,在请求完成之前将其撤销。

函数原型

#include <linux/aio_abi.h> 
#include <sys/syscall.h> 
#include <unistd.h> 

long io_cancel(aio_context_t ctx_id, struct iocb *iocb, struct io_event *result);
  • 参数说明:

    • ctx_id: 用于标识异步 I/O 上下文,由 io_setup 创建。

    • iocb: 指向要取消的异步 I/O 控制块(struct iocb)。

    • result: 指向 struct io_event,取消操作的结果会写入其中。

  • 返回值:

    • 成功返回 0,表示请求已取消。

    • 如果请求已完成或无法取消,返回负值,表示错误码。

io_getevents

io_getevents 是 Linux 异步 I/O (AIO) 接口中的一个系统调用,用于检索异步 I/O 请求的完成事件。它允许用户从指定的 AIO 上下文中获取已完成的 I/O 操作的事件信息。

系统调用原型

#include <linux/aio_abi.h> 
#include <sys/syscall.h> 
#include <unistd.h> 

long io_getevents(aio_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);
  • 参数说明:

    • ctx_id:通过 io_setup 创建的 AIO 上下文标识符。

    • min_nr:等待返回的最少事件数。

    • nr:可以返回的最大事件数。

    • events:指向 io_event 结构体数组的指针,用于存储完成事件的信息。

    • timeout:指向 struct timespec 的指针,用于设置超时时间(可选,NULL 表示无限等待)。

  • 返回值:

    • 成功时返回已完成的事件数量。

    • 失败时返回负值,表示错误码。