linux下aio异步读写详解与实例
1.为什么会有异步I/O
aio异步读写是在linux内核2.6之后才正式纳⼊其标准。之所以会增加此模块,是因为众所周知我们计算机CPU的执⾏速度远⼤于I/O读写的执⾏速度,如果我们⽤传统的阻塞式或⾮阻塞式来操作I/O的话,那么我们在同⼀个程序中(不⽤多线程或多进程)就不能同时操作俩个以上的⽂件I/O,每次只能对⼀个⽂件进⾏I/O操作,很明显这样效率很低下(因为CPU速度远⼤于I/O操作的速度,所以当执⾏I/O时,CPU其实还可以做更多的事)。因此就诞⽣了相对⾼效的异步I/O
2.异步I/O的基本概念
所谓异步I/O即我们在调⽤I/O操作时(读或写)我们的程序不会阻塞在当前位置,⽽是在继续往下执⾏。例如当我们调⽤异步读API
aio_read()时,程序执⾏此代码之后会接着运⾏此函数下⾯的代码,并且与此同时程序也在进⾏刚才所要读的⽂件的读取⼯作,但是具体什么时候读完是不确定的
3.异步aio的基本API
API函数说明
aio_read异步读操作
aio_write异步写操作
aio_error检查异步请求的状态
aio_return获得异步请求完成时的返回值
aio_suspend挂起调⽤进程,直到⼀个或多个异步请求已完成
aio_cancel取消异步请求
lio_list发起⼀系列异步I/O请求
上述的每个API都要⽤aiocb结构体赖进⾏操作
aiocb的结构中常⽤的成员有
write的返回值struct aiocb
{
/
/要异步操作的⽂件描述符
int aio_fildes;
//⽤于lio操作时选择操作何种异步I/O类型
int aio_lio_opcode;
//异步读或写的缓冲区的缓冲区
volatile void *aio_buf;
//异步读或写的字节数
size_t aio_nbytes;
//异步通知的结构体
struct sigevent aio_sigevent;
}
4异步I/O操作的具体使⽤
(1)异步读aio_read
aio_read函数请求对⼀个⽂件进⾏读操作,所请求⽂件对应的⽂件描述符可以是⽂件,套接字,甚⾄管道其原型如下
int aio_read(struct aiocb *paiocb);
1
该函数请求对⽂件进⾏异步读操作,若请求失败返回-1,成功则返回0,并将该请求进⾏排队,然后就开始对⽂件的异步读操作需要注意的是,我们得先对aiocb结构体进⾏必要的初始化
具体实例如下
aio_read
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<aio.h>
#define BUFFER_SIZE 1024
int MAX_LIST = 2;
int main(int argc,char **argv)
{
//aio操作所需结构体
struct aiocb rd;
int fd,ret,couter;
fd = open("",O_RDONLY);
if(fd < 0)
{
perror("");
}
/
/将rd结构体清空
bzero(&rd,sizeof(rd));
//为rd.aio_buf分配空间
rd.aio_buf = malloc(BUFFER_SIZE + 1);
//填充rd结构体
rd.aio_fildes = fd;
rd.aio_nbytes =  BUFFER_SIZE;
rd.aio_offset = 0;
//进⾏异步读操作
ret = aio_read(&rd);
if(ret < 0)
{
perror("aio_read");
exit(1);
}
couter = 0;
//  循环等待异步读操作结束
while(aio_error(&rd) == EINPROGRESS)
{
printf("第%d次\n",++couter);
}
}
/
/获取异步读返回值
ret = aio_return(&rd);
printf("\n\n返回值为:%d",ret);
return 0;
}
上述实例中aiocb结构体⽤来表⽰某⼀次特定的读写操作,在异步读操作时我们只需要注意4点内容
1.确定所要读的⽂件描述符,并写⼊aiocb结构体中(下⾯⼏条⼀样不再赘余)
2.确定读所需的缓冲区
3.确定读的字节数
4.确定⽂件的偏移量
总结以上注意事项:基本上和我们的read函数所需的条件相似,唯⼀的区别就是多⼀个⽂件偏移量
值得注意的是上述代码中aio_error是⽤来获取其参数指定的读写操作的状态的
其原型如下
int aio_error(struct aiocb *aiopcb);
当其状态处于EINPROGRESS则I/O还没完成,当处于ECANCELLED则操作已被取消,发⽣错误返回-1
⽽aio_return则是⽤来返回其参数指定I/O操作的返回值
其原型如下
ssize_t aio_return(struct aiocb *paiocb);
如果操作没完成调⽤此函数,则会产⽣错误
特别提醒在编译上述程序时必须在编译时再加⼀个-lrt
上述代码运⾏结果如下
(2)异步写aio_write
aio_writr⽤来请求异步写操作
其函数原型如下
int aio_write(struct aiocb *paiocb);
aio_write和aio_read函数类似,当该函数返回成功时,说明该写请求以进⾏排队(成功0,失败-1)
其和aio_read调⽤时的区别是就是我们如果在打开⽂件是,flags设置了O_APPEND则我们在填充aiocb时不需要填充它的偏移量了具体实例如下
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<aio.h>
#define BUFFER_SIZE 1025
int main(int argc,char **argv)
{
//定义aio控制块结构体
struct aiocb wr;
int ret,fd;
char str[20] = {"hello,world"};
//置零wr结构体
bzero(&wr,sizeof(wr));
fd = open("",O_WRONLY | O_APPEND);    if(fd < 0)
{
perror("");
}
//为aio.buf申请空间
wr.aio_buf = (char *)malloc(BUFFER_SIZE);    if(wr.aio_buf == NULL)
{
perror("buf");
}
wr.aio_buf = str;
//填充aiocb结构
wr.aio_fildes = fd;
wr.aio_nbytes = 1024;
/
/异步写操作
ret = aio_write(&wr);
if(ret < 0)
{
perror("aio_write");
}
//等待异步写完成
while(aio_error(&wr) == EINPROGRESS)
{
printf("hello,world\n");
}
/
/获得异步写的返回值
ret = aio_return(&wr);
printf("\n\n\n返回值为:%d\n",ret);
return 0;
}
具体运⾏结果请读者⾃⼰去试试
(3)使⽤aio_suspend阻塞异步I/O
aio_suspend函数可以时当前进程挂起,知道有向其注册的异步事件完成为⽌
该函数原型如下
int aio_suspend(const struct aiocb *const cblist[],int n,const struct timespec *timeout);
第⼀个参数是个保存了aiocb块地址的数组,我们可以向其内添加想要等待阻塞的异步事件,第⼆个参数为向cblist注册的aiocb个数,第三个参数为等待阻塞的超时事件,NULL为⽆限等待
具体使⽤如下
suspend:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<aio.h>
#define BUFFER_SIZE 1024
int MAX_LIST = 2;
int main(int argc,char **argv)
{
//aio操作所需结构体
struct aiocb rd;
int fd,ret,couter;
//cblist链表
struct aiocb *aiocb_list[2];
fd = open("",O_RDONLY);
if(fd < 0)
{
perror("");
}
//将rd结构体清空
bzero(&rd,sizeof(rd));
//为rd.aio_buf分配空间
rd.aio_buf = malloc(BUFFER_SIZE + 1);
//填充rd结构体
rd.aio_fildes = fd;
rd.aio_nbytes =  BUFFER_SIZE;
rd.aio_offset = 0;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。