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.data
或io_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
表示无限等待)。
返回值:
成功时返回已完成的事件数量。
失败时返回负值,表示错误码。