linux aio 实现概览

----------
kernel version : 2.6.32.60
----------

 

I.aio
Asynchronous I/O帮助用户程序提高CPU和IO设备的利用率和提高程序性能,特别是在高负载的IO操作下。比如各种代理服务器,数据库,流服务器等等。

AIO可以一次性发出大量的read/write调用并且通过通用块层的IO调度来获得更好的性能,用户程序也可以减少过多的同步负载,还可以在业务逻辑中更灵活的进行并发控制和负载均衡。另外相对于其他实现如用户多线程后台同步,Glibc等实现也减少了线程的负载和上下文切换。

 

AIO是横架于整个内核的接口,它把所有的IO包括(本地设备,网络,管道等)以统一的异步接口提供给用户程序,每个子系统都针对接口实现自己的异步方案,而同步IO(Synchronous IO)只是在内核内部的”AIO+Blocking”.

以read为例来看"同步IO在内核实现为AIO+Blocking"
fs/read_write.c

ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
	struct iovec iov = { .iov_base = buf, .iov_len = len };
	struct kiocb kiocb;
	ssize_t ret;

	init_sync_kiocb(&kiocb, filp);
	kiocb.ki_pos = *ppos;
	kiocb.ki_left = len;

	for (;;) {
		ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);
		if (ret != -EIOCBRETRY)
			break;
		wait_on_retry_sync_kiocb(&kiocb);
	}

	if (-EIOCBQUEUED == ret)
		ret = wait_on_sync_kiocb(&kiocb);
	*ppos = kiocb.ki_pos;
	return ret;
}

EXPORT_SYMBOL(do_sync_read);

ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
	ssize_t ret;

	if (!(file->f_mode & FMODE_READ))
		return -EBADF;
	if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
		return -EINVAL;
	if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
		return -EFAULT;

	ret = rw_verify_area(READ, file, pos, count);
	if (ret >= 0) {
		count = ret;
		if (file->f_op->read)
			ret = file->f_op->read(file, buf, count, pos);
		else
			ret = do_sync_read(file, buf, count, pos);
		if (ret > 0) {
			fsnotify_access(file->f_path.dentry);
			add_rchar(current, ret);
		}
		inc_syscr(current);
	}

	return ret;
}

EXPORT_SYMBOL(vfs_read);

由以上代码可以看出,如果VFS提供同步read方法,则使用同步read方法;否则使用AIO+Blocking的do_sync_read

 

II.aio结构


aio的主要流程:
io_setup创建aio上下文
io_submit提交aio操作,并将aio上下文放入到work_queue中异步完成提交的aio操作
work_queue完成aio操作后将其入到事件回环缓存中,供io_getevents读取

 

III.io_setup
io_setup主要用于创建aio上下文:
  1.初始化kioctx结构
  2.分配Ring Buffer,用于存储完成AIO操作的事件

/* sys_io_setup:
 *	Create an aio_context capable of receiving at least nr_events.
 *	ctxp must not point to an aio_context that already exists, and
 *	must be initialized to 0 prior to the call.  On successful
 *	creation of the aio_context, *ctxp is filled in with the resulting 
 *	handle.  May fail with -EINVAL if *ctxp is not initialized,
 *	if the specified nr_events exceeds internal limits.  May fail 
 *	with -EAGAIN if the specified nr_events exceeds the user's limit 
 *	of available events.  May fail with -ENOMEM if insufficient kernel
 *	resources are available.  May fail with -EFAULT if an invalid
 *	pointer is passed for ctxp.  Will fail with -ENOSYS if not
 *	implemented.
 */
SYSCALL_DEFINE2(io_setup, unsigned, nr_events, aio_context_t __user *, ctxp)
{
	struct kioctx *ioctx = NULL;
	unsigned long ctx;
	long ret;

	ret = get_user(ctx, ctxp);
	if (unlikely(ret))
		goto out;

	ret = -EINVAL;
	if (unlikely(ctx || nr_events == 0)) {
		pr_debug("EINVAL: io_setup: ctx %lu nr_events %u\n",
		         ctx, nr_events);
		goto out;
	}

	ioctx = ioctx_alloc(nr_events);
	ret = PTR_ERR(ioctx);
	if (!IS_ERR(ioctx)) {
		ret = put_user(ioctx->user_id, ctxp);
		if (!ret)
			return 0;

		get_ioctx(ioctx); /* io_destroy() expects us to hold a ref */
		io_destroy(ioctx);
	}

out:
	return ret;
}

根据事件数创建aio上下文,并将user_id返回给用户空间,以后根据user_id使用该aio上下文

 

/* ioctx_alloc
 *	Allocates and initializes an ioctx.  Returns an ERR_PTR if it failed.
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中关闭AIO(异步输入输出)可以通过以下步骤实现: 1. 首先,确保在程序中调用了`aio_cancel`函数来取消所有未完成的异步操作。这将确保在关闭AIO之前,所有的异步操作都被终止。 2. 然后,使用`aio_error`函数来检查异步操作的错误状态。如果返回值为`EINPROGRESS`,表示异步操作仍在进行中,需要等待操作完成。 3. 使用`aio_return`函数来获取异步操作的返回值。如果返回值大于0,表示操作已完成,并且可以获取到返回的数据。 4. 最后,使用`aio_suspend`函数来等待所有异步操作完成。这将阻塞程序,直到所有异步操作都完成。 5. 在所有异步操作都完成后,可以关闭文件描述符和释放相关的资源。 以下是一个示例代码,展示了如何关闭AIO: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <aio.h> #include <fcntl.h> int main() { struct aiocb cb = {0}; int fd = open("test.txt", O_RDONLY); if (-1 == fd) { printf("文件打开失败:%m\n"); exit(-1); } printf("文件打开成功!\n"); // 异步读取文件数据 cb.aio_fildes = fd; cb.aio_nbytes = BUFF_SIZE; cb.aio_offset = 0; int r = aio_read(&cb); if (-1 == r) { printf("异步读取失败:%m\n"); close(fd); exit(-2); } printf("异步读取成功!\n"); // 取消未完成的异步操作 aio_cancel(fd, &cb); // 等待所有异步操作完成 aio_suspend(&cb, 1, NULL); // 获取异步操作的返回值 r = aio_return(&cb); if (r > 0) { printf("拿到了数据: r:%d bytes, data: %s\n", r, cb.aio_buf); } // 关闭文件描述符和释放内存 close(fd); free(cb.aio_buf); return 0; } ``` 请注意,以上代码仅展示了关闭AIO的基本步骤,并不包含完整的错误处理和资源释放。在实际应用中,需要根据具体情况进行适当的错误处理和资源管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值