今天在看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…