赞
踩
在调研 fio的实现时,接触了libaio的使用方式。由于fio 的io engine发送及接受数据的流程是按照liaio库的方式进行的。所以初步使用了libaio。现总结如下。
几点说明
本文的重点在于libaio的使用方式。所以
linux kernel 提供了5个系统调用来实现异步IO。文中最后介绍的是包装了这些系统调用的用户空间的函数。
AIO系统调用总共五个,后面会一一介绍。
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 *cbp[]);
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);
#define _GNU_SOURCE /* syscall() is not POSIX */ #include <stdio.h> /* for perror() */ #include <unistd.h> /* for syscall() */ #include <sys/syscall.h> /* for __NR_* definitions */ #include <linux/aio_abi.h> /* for AIO types and constants */ inline int io_setup(unsigned nr, aio_context_t *ctxp) { return syscall(__NR_io_setup, nr, ctxp); } inline int io_destroy(aio_context_t ctx) { return syscall(__NR_io_destroy, ctx); } int main() { aio_context_t ctx; int ret; ctx = 0; ret = io_setup(128, &ctx); if (ret < 0) { perror("io_setup error"); return -1; } printf("after io_setup ctx:%Ld\n",ctx); ret = io_destroy(ctx); if (ret < 0) { perror("io_destroy error"); return -1; } printf("after io_destroy ctx:%Ld\n",ctx); return 0; }
系统调用io_setup会创建一个所谓的"AIO上下文"(即aio_context,后文也叫‘AIO context’等)结构体到在内核中。aio_context是用以内核实现异步AIO的数据结构。它其实是一个无符号整形,位于头文件 /usr/include/linux/aio_abi.h。
typedef unsigned long aio_context_t;
每个进程都可以有多个aio_context_t。传入io_setup的第一个参数在这里是128,表示同时驻留在上下文中的IO请求的个数;第二个参数是一个指针,内核会填充这个值。
io_destroy的作用是销毁这个上下文aio_context_t。
上面的例子很简单,创建一个aio_context_t并销毁。
#define _GNU_SOURCE /* syscall() is not POSIX */ #include <stdio.h> /* for perror() */ #include <unistd.h> /* for syscall() */ #include <sys/syscall.h> /* for __NR_* definitions */ #include <linux/aio_abi.h> /* for AIO types and constants */ #include <fcntl.h> /* O_RDWR */ #include <string.h> /* memset() */ #include <inttypes.h> /* uint64_t */ inline int io_setup(unsigned nr, aio_context_t *ctxp) { return syscall(__NR_io_setup, nr, ctxp); } inline int io_destroy(aio_context_t ctx) { return syscall(__NR_io_destroy, ctx); } inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) { return syscall(__NR_io_submit, ctx, nr, iocbpp); } inline 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); } int main() { aio_context_t ctx; struct iocb cb; struct iocb *cbs[1]; char data[4096]; struct io_event events[1]; int ret; int fd; int i ; for(i=0;i<4096;i++) { data[i]=i%50+60; } fd = open("./testfile", O_RDWR | O_CREAT,S_IRWXU); if (fd < 0) { perror("open error"); return -1; } ctx = 0; ret = io_setup(128, &ctx); printf("after io_setup ctx:%ld",ctx); if (ret < 0) { perror("io_setup error"); return -1; } /* setup I/O control block */ memset(&cb, 0, sizeof(cb)); cb.aio_fildes = fd; cb.aio_lio_opcode = IOCB_CMD_PWRITE;/* command-specific options */ cb.aio_buf = (uint64_t)data; cb.aio_offset = 0; cb.aio_nbytes = 4096; cbs[0] = &cb; ret = io_submit(ctx, 1, cbs); if (ret != 1) { if (ret < 0) perror("io_submit error"); else fprintf(stderr, "could not sumbit IOs"); return -1; } /* get the reply */ ret = io_getevents(ctx, 1, 1, events, NULL); printf("%d\n", ret); struct iocb * result = (struct iocb *)events[0].obj; printf("reusult:%Ld",result->aio_buf); ret = io_destroy(ctx); if (ret < 0) { perror("io_destroy error"); return -1; } return 0; }
IOCB_CMD_PREAD 读; 对应系统调用pread().
IOCB_CMD_PWRITE 写,对应系统调用pwrite().
IOCB_CMD_FSYNC 同步文件数据到磁盘,对应系统调用fsync()
IOCB_CMD_FDSYNC 同步文件数据到磁盘,对应系统调用fdatasync()
IOCB_CMD_PREADV 读,对应系统调用readv()
IOCB_CMD_PWRITEV 写,对应系统调用writev()
IOCB_CMD_NOOP 只是内核使用
A. ret = (提交的iocb的数目) 表示所有的iocb都被接受并处理
B. 0 < ret < (提交的iocb的数目) io_submit() 系统调用会从传入的cbs中一个一个处理iocb,如果提交的某个iocb失败,将停止并且返回iocb的索引号。没办法知晓错误的具体原因,但是如果第一个iocb提交失败,参看C条。
C. ret < 0 有两种原因:
1. 在io_submit()开始之前发生了某种错误(e.g.比如AIO context非法).
2. 提交第一个iocb(cbx[0])失败
int io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *timeout)
a) 使用哪一个AIO上下文(变量ctx)
b) 内核把这个变量放入哪个内存位置 (变量events)
c) events的最小个数(变量min_nr,),如果完成的iocb的个数比这个值要小io_getevents会阻塞,直到达到这个值, 参看第e条查看阻塞时间。
d) 想要获取的events的最大个数(变量nr)。
e) 如果获取不到足够的events,而又不想永久等待。可以指定相对时间(timeout)到最后一个参数,
如果timeout为NULL,表示永久等待。
如果timeout为0,io_getevents()不阻塞 。
转自: https://blog.csdn.net/zdy0_2004/article/details/60881557
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。