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的结构中常用的成员有

struct aiocb
{//要异步操作的文件描述符int aio_fildes;//用于lio操作时选择操作何种异步I/O类型int aio_lio_opcode;//异步读或写的缓冲区的缓冲区volatile void *aio_buf;//异步读或写的字节数size_t aio_nbytes;//异步通知的结构体struct sigevent aio_sigevent;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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 1024int MAX_LIST = 2;int main(int argc,char **argv)
{//aio操作所需结构体struct aiocb rd;int fd,ret,couter;fd = open("test.txt",O_RDONLY);if(fd < 0){perror("test.txt");}//将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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

上述实例中aiocb结构体用来表示某一次特定的读写操作,在异步读操作时我们只需要注意4点内容
1.确定所要读的文件描述符,并写入aiocb结构体中(下面几条一样不再赘余)
2.确定读所需的缓冲区
3.确定读的字节数
4.确定文件的偏移量
总结以上注意事项:基本上和我们的read函数所需的条件相似,唯一的区别就是多一个文件偏移量

值得注意的是上述代码中aio_error是用来获取其参数指定的读写操作的状态的
其原型如下

int aio_error(struct aiocb *aiopcb);
  • 1

当其状态处于EINPROGRESS则I/O还没完成,当处于ECANCELLED则操作已被取消,发生错误返回-1

而aio_return则是用来返回其参数指定I/O操作的返回值
其原型如下

ssize_t aio_return(struct aiocb *paiocb);
  • 1

如果操作没完成调用此函数,则会产生错误

特别提醒在编译上述程序时必须在编译时再加一个-lrt

上述代码运行结果如下

(2)异步写aio_write

aio_writr用来请求异步写操作
其函数原型如下

int aio_write(struct aiocb *paiocb);
  • 1

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<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<aio.h>#define BUFFER_SIZE 1025int main(int argc,char **argv)
{//定义aio控制块结构体struct aiocb wr;int ret,fd;char str[20] = {"hello,world"};//置零wr结构体bzero(&wr,sizeof(wr));fd = open("test.txt",O_WRONLY | O_APPEND);if(fd < 0){perror("test.txt");}//为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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

具体运行结果请读者自己去试试

(3)使用aio_suspend阻塞异步I/O

aio_suspend函数可以时当前进程挂起,知道有向其注册的异步事件完成为止
该函数原型如下

int aio_suspend(const struct aiocb *const cblist[],int n,const struct timespec *timeout);
  • 1

第一个参数是个保存了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 1024int 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("test.txt",O_RDONLY);if(fd < 0){perror("test.txt");}//将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;//将读fd的事件注册aiocb_list[0] = &rd;//进行异步读操作ret = aio_read(&rd);if(ret < 0){perror("aio_read");exit(1);}couter = 0;
//  循环等待异步读操作结束while(aio_error(&rd) == EINPROGRESS){printf("第%d次\n",++couter);}printf("我要开始等待异步读事件完成\n");//阻塞等待异步读事件完成ret = aio_suspend(aiocb_list,MAX_LIST,NULL);//获取异步读返回值ret = aio_return(&rd);printf("\n\n返回值为:%d\n",ret);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

(4)lio_listio函数

aio同时还为我们提供了一个可以发起多个或多种I/O请求的接口lio_listio
这个函数效率很高,因为我们只需一次系统调用(一次内核上下位切换)就可以完成大量的I/O操作
其函数原型如下

int lio_listio(int mode,struct aiocb *list[],int nent,struct sigevent *sig);
  • 1

第一个参数mode可以有俩个实参,LIO_WAIT和LIO_NOWAIT,前一个会阻塞该调用直到所有I/O都完成为止,后一个则会挂入队列就返回

具体实例如下
lio_listio

#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 1025int MAX_LIST = 2;int main(int argc,char **argv)
{struct aiocb *listio[2];struct aiocb rd,wr;int fd,ret;//异步读事件fd = open("test1.txt",O_RDONLY);if(fd < 0){perror("test1.txt");}bzero(&rd,sizeof(rd));rd.aio_buf = (char *)malloc(BUFFER_SIZE);if(rd.aio_buf == NULL){perror("aio_buf");}rd.aio_fildes = fd;rd.aio_nbytes = 1024;rd.aio_offset = 0;rd.aio_lio_opcode = LIO_READ;   ///lio操作类型为异步读//将异步读事件添加到list中listio[0] = &rd;//异步些事件fd = open("test2.txt",O_WRONLY | O_APPEND);if(fd < 0){perror("test2.txt");}bzero(&wr,sizeof(wr));wr.aio_buf = (char *)malloc(BUFFER_SIZE);if(wr.aio_buf == NULL){perror("aio_buf");}wr.aio_fildes = fd;wr.aio_nbytes = 1024;wr.aio_lio_opcode = LIO_WRITE;   ///lio操作类型为异步写//将异步写事件添加到list中listio[1] = &wr;//使用lio_listio发起一系列请求ret = lio_listio(LIO_WAIT,listio,MAX_LIST,NULL);//当异步读写都完成时获取他们的返回值ret = aio_return(&rd);printf("\n读返回值:%d",ret);ret = aio_return(&wr);printf("\n写返回值:%d",ret);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

5.I/O完成时进行异步通知

当我们的异步I/O操作完成之时,我们可以通过信号通知我们的进程也可用回调函数来进行异步通知,接下来我会为大家主要介绍以下回调函数来进行异步通知,关于信号通知有兴趣的同学自己去学习吧

使用回调进行异步通知

该种通知方式使用一个系统回调函数来通知应用程序,要想完成此功能,我们必须在aiocb中设置我们想要进行异步回调的aiocb指针,以用来回调之后表示其自身

实例如下
aio线程回调通知

#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>
#include<unistd.h>#define BUFFER_SIZE 1025void aio_completion_handler(sigval_t sigval)
{//用来获取读aiocb结构的指针struct aiocb *prd;int ret;prd = (struct aiocb *)sigval.sival_ptr;printf("hello\n");//判断请求是否成功if(aio_error(prd) == 0){//获取返回值ret = aio_return(prd);printf("读返回值为:%d\n",ret);}
}int main(int argc,char **argv)
{int fd,ret;struct aiocb rd;fd = open("test.txt",O_RDONLY);if(fd < 0){perror("test.txt");}//填充aiocb的基本内容bzero(&rd,sizeof(rd));rd.aio_fildes = fd;rd.aio_buf = (char *)malloc(sizeof(BUFFER_SIZE + 1));rd.aio_nbytes = BUFFER_SIZE;rd.aio_offset = 0;//填充aiocb中有关回调通知的结构体sigeventrd.aio_sigevent.sigev_notify = SIGEV_THREAD;//使用线程回调通知rd.aio_sigevent.sigev_notify_function = aio_completion_handler;//设置回调函数rd.aio_sigevent.sigev_notify_attributes = NULL;//使用默认属性rd.aio_sigevent.sigev_value.sival_ptr = &rd;//在aiocb控制块中加入自己的引用//异步读取文件ret = aio_read(&rd);if(ret < 0){perror("aio_read");}printf("异步读以开始\n");sleep(1);printf("异步读结束\n");return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

线程会掉是通过使用aiocb结构体中的aio_sigevent结构体来控制的,
其定义如下

struct sigevent
{sigval_t sigev_value;int sigev_signo;int sigev_notify;union {int _pad[SIGEV_PAD_SIZE];int _tid;struct {void (*_function)(sigval_t);void *_attribute;   /* really pthread_attr_t */} _sigev_thread;} _sigev_un;
}#define sigev_notify_function   _sigev_un._sigev_thread._function
#define sigev_notify_attributes _sigev_un._sigev_thread._attribute
#define sigev_notify_thread_id   _sigev_un._tid
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

linux下aio异步读写详解与实例相关推荐

  1. linux下find,xargs命令详解和实例

    在我们平时的系统管理中,查找文件是时常需要的一个操作,在Linux系统中有2个命令是必须要掌握的,如果我们真正掌握了这个命令已经他的常用参数的话,我们可以在工作中游刃有余,下面是我在网上找到的一点资料 ...

  2. linux下top命令参数详解

    linux下top命令参数详解 top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.下面详细介绍它的使用方法. 内存信息.内容如下: ...

  3. linux下sort命令使用详解---linux将文本文件内容加以排序命令

    转载自:http://www.cnblogs.com/hitwtx/archive/2011/12/03/2274592.html linux下sort命令使用详解---linux将文本文件内容加以排 ...

  4. Linux下的awk用法详解

    Linux下的awk用法详解 一.awk介绍 二.awk的语法 三.awk常见用法 四.awk其他用法 五.awk语言特性 一.awk介绍 1.AWK 是一种处理文本文件的语言,是一个强大的文本分析工 ...

  5. linux输入一个用户看是否在工作,linux下的用户管理详解

    linux下的用户管理详解 useradd 命令详解 添加用户 想要对linux下面的帐号了解的话首先必须要了解的4个配置文件[root@localhost /]# cat /etc/passwd 首 ...

  6. Linux下find命令用法详解

    Linux下find命令用法详解 学神VIP烟火 学神IT教育:XueGod-IT 最负责任的线上直播教育平台 本文作者为VIP学员 烟火 第一部分:根据文件名查找 1.在当前目录查找名称为test的 ...

  7. linux中which命令详解,Linux下which命令使用详解(转)

    我们经常在linux要查找某个文件,但不知道放在哪里了,可以使用下面的一些命令来搜索: which 查看可执行文件的位置. whereis 查看文件的位置. locate 配合数据库查看文件位置. f ...

  8. linux下测试ftp传输,linux下ftp命令使用详解---linux文件传输ftp命令

    linux下ftp命令使用详解---linux文件传输ftp命令 上一篇 / 下一篇  2010-12-18 09:15:35 / 个人分类:Linux ftp(file transfer proto ...

  9. rm命令linux00,linux下rm命令使用详解 | Soo Smart!

    linux下rm命令使用详解---linux删除文件或目录命令 用户可以用rm命令删除不需要的文件.该命令的功能为删除一个目录中的一个或多个文件或目录,它也可以将某个目录及其下的所有文件及子目录均删除 ...

最新文章

  1. android模糊后面视频,在安卓手机上怎么制作中间是横视频上下是模糊效果的竖视频?手机视频短片制作...
  2. 基于开源jabber(XMPP)架设内部即时通讯服务的解决方案
  3. (八):构建WineLib DLL
  4. ICON素材|装饰图标设计的技巧
  5. SYNPROXY:廉价的抗 DoS 攻击方案
  6. Allegro cadence下载安装
  7. 全屋WiFi方案:Mesh路由器组网和AC+AP
  8. TransactionSystemException
  9. [渝粤教育] 南京师范大学 中国古代文学(一) 参考 资料
  10. MPQ文件系统优化(续)
  11. linux无线网卡ucod文件,基于Linux的无线网卡驱动程序
  12. 好斌c语言教程,C语言学习大纲__斌(讲解).doc.doc
  13. win10配置计算机时强制关机,老鸟讲解Win10设置远程系统强制关机的详尽处理要领...
  14. 动态规划---01背包问题详解
  15. (二)Linux ALSA 音频系统:逻辑设备篇
  16. wep 与 wpa 的区别
  17. 安卓国际化之strings.xml导入Excel表格及从excel恢复到Strings.xml中
  18. 服务器禁用网络协议,启用或禁用服务器网络协议
  19. 赢得值系列1:赢得值管理的历史
  20. Fiddler抓包移动端https请求

热门文章

  1. android ip地址扫描,Android:手机扫描局域网所有ip,并进行socket通讯
  2. 【数学与算法】【分段三次Hermite插值】和【分段三次样条插值】
  3. Java 7 源码学习系列(一)——String
  4. Java魔法堂:注解用法详解——@SuppressWarnings
  5. 谷歌chrome浏览器的源码分析(四)
  6. OpenCV学习笔记(五十一)——imge stitching图像拼接stitching OpenCV学习笔记(五十二)——号外:OpenCV 2.4.1 又出来了。。。。。 OpenCV学习笔记(五
  7. 概率统计:第二章 随机变量及其分布
  8. 编程的首要原则(s)是什么?
  9. 【OpenCV3】双线性插值
  10. java服务端的 极光推送