日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux下aio异步读写详解与实例

發布時間:2025/3/21 linux 75 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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的結構中常用的成員有

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异步读写详解与实例的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。