异步I/O(AIO,asynchronous I/O),顾名思义,就是异步操作I/O。不像read/write那样会阻塞进程,必须等待其返回才能继续执行其它指令。
对于I/O密集性应用(如传统关系型数据库)异步I/O可以带来很大的性能提升。MySQL和Nginx已支持Native AIO。
1. 接口
linux native aio对使用O_DIRECT标识打开的文件会造成如下限制(如果无O_DIRECT标识,在调用io_submit时,会同步完成IO操作):AIO方式读写文件时,无法利用操作系统对文件的缓存,只能从磁盘读写
读写缓冲区的地址、内容的大小、文件偏移必须是扇区的倍数(通常是512字节)
2. 示例
#include#include#include#include#include#include#include
#define FILEPATH "./test.txt"
int main()
{
io_context_t context;
struct iocb io[2], *p[2]={&io[0], &io[1]};
struct io_event e[2];
unsigned nr_events = 10;
struct timespec timeout;
timeout.tv_sec = 0;
timeout.tv_nsec = 10000000;
char *wbuf = 0, *rbuf = 0;
int wbuflen = 512*1024*1024;
int rbuflen = wbuflen+1;
posix_memalign((void**)&wbuf, 512, wbuflen);
posix_memalign((void**)&rbuf, 512, rbuflen);
memset(wbuf, 'a', wbuflen);
memset(rbuf, 0, rbuflen);
memset(&context, 0, sizeof(io_context_t));
int ret = 0, comp_num = 0, i = 0;
int fd = open(FILEPATH, O_CREAT|O_RDWR|O_DIRECT, 0644);
if(fd < 0)
{
printf("open file failed !\n");
return 0;
}
if( 0 != io_setup(nr_events, &context) ){
printf("io_setup error:%d\n", errno);
}
io_prep_pwrite(&io[0], fd, wbuf, wbuflen, 0);
io_prep_pread(&io[1], fd, rbuf, rbuflen-1, 0);
if((ret = io_submit(context,2,p)) != 2)
{
printf("io_submit error:%d\n", ret);
io_destroy(context);
return -1;
}
while(true)
{
ret = io_getevents(context,2,2,e,&timeout);
if(ret < 0)
{
printf("io_getevents error:%d\n", ret);
break;
}
if(ret > 0)
{
comp_num += ret;
for( i = 0;i < ret; ++i)
{
printf("result,res2:%d, res:%d\n", e[i].res2, e[i].res);
}
}
if(comp_num >= 2)
{
printf("done !\n");
break;
}
printf("have not done !\n");
}
return 0;
}
3. AIO与epoll
在使用AIO时,需要通过系统调用io_getevents获取已经完成的IO事件,而系统调用io_getevents是阻塞的,所以有2种方式:
(1) 使用多线程,用专门的线程调用io_getevents,参考MySQL5.5及以上版本;
(2) 对于单线程程序,可以通过epoll来使用AIO;但需要系统调用eventfd的支持,在2.6.22之后的内核才支持。
eventfd 是 Linux-native aio 其中的一个 API,用来生成 file descriptors,这些 file descriptors 可为应用程序提供更高效 “等待/通知” 的事件机制。和 pipe 作用相似,但比 pipe 更好,一方面它只用到一个 file descriptor(pipe 要用两个),节省了内核资源;另一方面,eventfd 的缓冲区管理要简单得多,pipe 需要不定长的缓冲区,而 eventfd 全部缓冲只有定长 8 bytes。
关于AIO与epoll的结合可以参考nginx 0.8.x稳定版对linux aio的支持。
4. AIO与Direct IO
AIO需要与direct IO结合(为什么需要这样结合?)。Direct IO是数据直接在用户地址空间的缓冲区和磁盘之间直接进行传输,完全不需要页缓存的支持。
Direct IO读写操作都有硬性的要求,所有操作系统都一致:数据传输的开始点,即文件和设备的偏移量,必须是扇区大小的整数倍
待传递数据的长度必须是扇区大小的整数倍
用于传递数据的缓冲区,其内存边界必须对齐为扇区大小的整数倍
前两个限制就是为了保证读写的都是一个个完整的扇区。第一个限制要求从一个扇区的开始读写,第二个限制要求正好到一个扇区完截止。第三个限制则是为了保证读写一个扇区时不发生页错误(page fault)。
5. 参考