Linux内置的异步AIO使用

567 阅读2分钟

今天在看libkcapi源码的时候,看到了一个struct iocb的结构。注释说这个是什么AIO相关的数据结构。上网查了一下,这是Linux内置的异步I/O机制的一个概念。

三月份的时候,写了一篇关于Linux IO的select、poll、epoll接口的使用。异步IO也比较容易理解了,就是为了使程序不阻塞而实现的一种机制。

AIO的实现也就是提交一个IO请求,然后流程继续处理,过一段时间来查询一下提交IO的状态,确定IO操作是否完成。

实现及接口

AIO相关的系统调用实现在内核fs/aio.c文件中,头文件是<linux/aio_abi.h>。AIO提供了5个API:

#include <linux/aio_abi.h>
int io_setup(unsigned nr_events, aio_context_t *ctxp);
int io_destroy(aio_context_t ctx);
int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp);
int io_cancel(aio_context_t ctx, struct iocb *, struct io_event *result);
int io_getevents(aio_context_t ctx, long min_nr, long nr,
    struct io_event *events, struct timespec *timeout);

每个IO请求(对应结构体struct iocb)都被提交到一个AIO的上下文(对应aio_context_t ,其实是一个数字)中,上下文通过 io_setup创建, io_destroy销毁。

io_submit函数一次可以提交多个IO请求到指定的上下文中。

在提交IO请求之后,可以去处理其它的事情。过段时间,通过io_getevents来获取一下IO请求状态。需要指定前面的上下文, 然后就是一个iocb的缓存区,等待完成IO的最少、最大数目。如果没有达到最少的IO数,则会阻塞知道满足条件。当然,也可以设置超时时间。

具体的结构定义这边就不详解了。

要使用Linux AIO的机制有几种方法:

  • 使用内核的系统调用
  • 使用用户空间上的libaio库
  • 自己在用户态模拟假的AIO,不需要内核支持。 目前有一个实现 librt

也就是说,我们在用户态不能直接使用上面的那些接口,而是使用libaio这个库提供的接口来实现,或者使用系统调用。

下面是一个简单的例子,自己封装个同名的接口,内部使用系统调用,通过AIO机制往文件写入一段字符串。


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

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

 int io_setup(unsigned nr, aio_context_t *ctxp) {
	return syscall(__NR_io_setup, nr, ctxp);
}

 int io_destroy(aio_context_t ctx) {
	return syscall(__NR_io_destroy, ctx);
}

 int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {
	return syscall(__NR_io_submit, ctx, nr, iocbpp);
}

 int io_getevents(aio_context_t ctx, long min_nr, long max_nr,
		struct io_event *events, struct timespec *timeout) {
	return syscall(__NR_io_getevents, ctx, min_nr, max_nr, events, timeout);
}


#define MAX_IO_NUM (10)
int main(const int argc, char *argv[])
{
    if (argc < 3)
    {
        printf("param less 3\r\n");
        exit(1);
    }

    char *data = argv[1];
    char *file_name = argv[2];


    printf("get data %s file_name %s\r\n", data, file_name);

    int fd;
    aio_context_t ctx;
    struct iocb io;
    struct iocb *pio[1] = {&io};
    struct io_event e[1];
    struct timespec timeout;

    memset(&ctx, 0, sizeof(ctx));
    if (io_setup(MAX_IO_NUM, &ctx))
    {
        printf("io setup error\r\n");
        exit(1);
    }

    fd = open(file_name, O_CREAT|O_RDWR);
    if (fd < 0)
    {
        perror("open error");
        io_destroy(ctx);
        exit(1);
    }

    memset(&io, 0, sizeof(io));
	io.aio_fildes = fd;
	io.aio_lio_opcode = IOCB_CMD_PWRITE;

//	io.aio_reqprio = 0;
	io.aio_buf = (uint64_t)data;
	io.aio_nbytes = strlen(data);
	io.aio_offset = 0;

   // io.aio_data = data;
    if (io_submit(ctx, 1, pio) != 1)
    {
        close(fd);
        io_destroy(ctx);
        perror("io submit error");
        exit(1);
    }

    timeout.tv_sec = 1;
    int count = 1;
    while (1)
    {
        printf("start get events %d times\r\n", count++);
        if (io_getevents(ctx, 1, 1, e, &timeout) == 1)
        {
            close(fd);
            printf("get it\r\n");
            break;
        }

    }

    io_destroy(ctx);

    return 0;
}

效果如下:

root@keep-VirtualBox:/media/sf_VM_SHARE/code/blog_code/linux-kernel# ./aio 222222222222222222222222222 /tmp/2.txt
get data 222222222222222222222222222 file_name /tmp/2.txt
start get events 1 times
get it
root@keep-VirtualBox:/media/sf_VM_SHARE/code/blog_code/linux-kernel# more /tmp/2.txt
222222222222222222222222222
root@keep-VirtualBox:/media/sf_VM_SHARE/code/blog_code/linux-kernel#
root@keep-VirtualBox:/media/sf_VM_SHARE/code/blog_code/linux-kernel#


可以看到在程序执行完之后, 文件有内容了。

好了,又Get 一个新知识。

参考链接: oxnz.github.io/2016/10/13/…

更多

前段时间写的一个测试框架代码初版基本完成了,这两周一直在写文档, 不断的修改演化,画那个架构图画的有点儿晕了。正常的话,下周也要转正了吧,还得写个ppt,写文档真的也是一个能力啊,好好加油!

晚上回来出去跑了个步,然后再回来洗个澡。就把手机丢一边,专心搞了这篇文章,时间贼快啊, 23:30了,晚安!


行动,才不会被动!

欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。

博客地址: fishmwei.github.io

掘金主页: juejin.cn/user/208432…