linux下aio异步读写详解与实例
1.為什么會(huì)有異步I/O
aio異步讀寫是在linux內(nèi)核2.6之后才正式納入其標(biāo)準(zhǔn)。之所以會(huì)增加此模塊,是因?yàn)楸娝苤覀冇?jì)算機(jī)CPU的執(zhí)行速度遠(yuǎn)大于I/O讀寫的執(zhí)行速度,如果我們用傳統(tǒng)的阻塞式或非阻塞式來操作I/O的話,那么我們?cè)谕粋€(gè)程序中(不用多線程或多進(jìn)程)就不能同時(shí)操作倆個(gè)以上的文件I/O,每次只能對(duì)一個(gè)文件進(jìn)行I/O操作,很明顯這樣效率很低下(因?yàn)镃PU速度遠(yuǎn)大于I/O操作的速度,所以當(dāng)執(zhí)行I/O時(shí),CPU其實(shí)還可以做更多的事)。因此就誕生了相對(duì)高效的異步I/O
2.異步I/O的基本概念
所謂異步I/O即我們?cè)谡{(diào)用I/O操作時(shí)(讀或?qū)?我們的程序不會(huì)阻塞在當(dāng)前位置,而是在繼續(xù)往下執(zhí)行。例如當(dāng)我們調(diào)用異步讀API aio_read()時(shí),程序執(zhí)行此代碼之后會(huì)接著運(yùn)行此函數(shù)下面的代碼,并且與此同時(shí)程序也在進(jìn)行剛才所要讀的文件的讀取工作,但是具體什么時(shí)候讀完是不確定的
3.異步aio的基本API
| aio_read | 異步讀操作 |
| aio_write | 異步寫操作 |
| aio_error | 檢查異步請(qǐng)求的狀態(tài) |
| aio_return | 獲得異步請(qǐng)求完成時(shí)的返回值 |
| aio_suspend | 掛起調(diào)用進(jìn)程,直到一個(gè)或多個(gè)異步請(qǐng)求已完成 |
| aio_cancel | 取消異步請(qǐng)求 |
| lio_list | 發(fā)起一系列異步I/O請(qǐng)求 |
上述的每個(gè)API都要用aiocb結(jié)構(gòu)體賴進(jìn)行操作
aiocb的結(jié)構(gòu)中常用的成員有
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
4異步I/O操作的具體使用
(1)異步讀aio_read
aio_read函數(shù)請(qǐng)求對(duì)一個(gè)文件進(jìn)行讀操作,所請(qǐng)求文件對(duì)應(yīng)的文件描述符可以是文件,套接字,甚至管道其原型如下
int aio_read(struct aiocb *paiocb);- 1
該函數(shù)請(qǐng)求對(duì)文件進(jìn)行異步讀操作,若請(qǐng)求失敗返回-1,成功則返回0,并將該請(qǐng)求進(jìn)行排隊(duì),然后就開始對(duì)文件的異步讀操作
需要注意的是,我們得先對(duì)aiocb結(jié)構(gòu)體進(jìn)行必要的初始化
具體實(shí)例如下
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操作所需結(jié)構(gòu)體struct aiocb rd;int fd,ret,couter;fd = open("test.txt",O_RDONLY);if(fd < 0){perror("test.txt");}//將rd結(jié)構(gòu)體清空bzero(&rd,sizeof(rd));//為rd.aio_buf分配空間rd.aio_buf = malloc(BUFFER_SIZE + 1);//填充rd結(jié)構(gòu)體rd.aio_fildes = fd;rd.aio_nbytes = BUFFER_SIZE;rd.aio_offset = 0;//進(jìn)行異步讀操作ret = aio_read(&rd);if(ret < 0){perror("aio_read");exit(1);}couter = 0; // 循環(huán)等待異步讀操作結(jié)束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
上述實(shí)例中aiocb結(jié)構(gòu)體用來表示某一次特定的讀寫操作,在異步讀操作時(shí)我們只需要注意4點(diǎn)內(nèi)容
1.確定所要讀的文件描述符,并寫入aiocb結(jié)構(gòu)體中(下面幾條一樣不再贅余)
2.確定讀所需的緩沖區(qū)
3.確定讀的字節(jié)數(shù)
4.確定文件的偏移量
總結(jié)以上注意事項(xiàng):基本上和我們的read函數(shù)所需的條件相似,唯一的區(qū)別就是多一個(gè)文件偏移量
值得注意的是上述代碼中aio_error是用來獲取其參數(shù)指定的讀寫操作的狀態(tài)的
其原型如下
- 1
當(dāng)其狀態(tài)處于EINPROGRESS則I/O還沒完成,當(dāng)處于ECANCELLED則操作已被取消,發(fā)生錯(cuò)誤返回-1
而aio_return則是用來返回其參數(shù)指定I/O操作的返回值
其原型如下
- 1
如果操作沒完成調(diào)用此函數(shù),則會(huì)產(chǎn)生錯(cuò)誤
特別提醒在編譯上述程序時(shí)必須在編譯時(shí)再加一個(gè)-lrt
上述代碼運(yùn)行結(jié)果如下
(2)異步寫aio_write
aio_writr用來請(qǐng)求異步寫操作
其函數(shù)原型如下
- 1
aio_write和aio_read函數(shù)類似,當(dāng)該函數(shù)返回成功時(shí),說明該寫請(qǐng)求以進(jìn)行排隊(duì)(成功0,失敗-1)
其和aio_read調(diào)用時(shí)的區(qū)別是就是我們?nèi)绻诖蜷_文件是,flags設(shè)置了O_APPEND則我們?cè)谔畛鋋iocb時(shí)不需要填充它的偏移量了
具體實(shí)例如下
- 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
具體運(yùn)行結(jié)果請(qǐng)讀者自己去試試
(3)使用aio_suspend阻塞異步I/O
aio_suspend函數(shù)可以時(shí)當(dāng)前進(jìn)程掛起,知道有向其注冊(cè)的異步事件完成為止
該函數(shù)原型如下
- 1
第一個(gè)參數(shù)是個(gè)保存了aiocb塊地址的數(shù)組,我們可以向其內(nèi)添加想要等待阻塞的異步事件,第二個(gè)參數(shù)為向cblist注冊(cè)的aiocb個(gè)數(shù),第三個(gè)參數(shù)為等待阻塞的超時(shí)事件,NULL為無限等待
具體使用如下
suspend:
- 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函數(shù)
aio同時(shí)還為我們提供了一個(gè)可以發(fā)起多個(gè)或多種I/O請(qǐng)求的接口lio_listio
這個(gè)函數(shù)效率很高,因?yàn)槲覀冎恍枰淮蜗到y(tǒng)調(diào)用(一次內(nèi)核上下位切換)就可以完成大量的I/O操作
其函數(shù)原型如下
- 1
第一個(gè)參數(shù)mode可以有倆個(gè)實(shí)參,LIO_WAIT和LIO_NOWAIT,前一個(gè)會(huì)阻塞該調(diào)用直到所有I/O都完成為止,后一個(gè)則會(huì)掛入隊(duì)列就返回
具體實(shí)例如下
lio_listio
- 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完成時(shí)進(jìn)行異步通知
當(dāng)我們的異步I/O操作完成之時(shí),我們可以通過信號(hào)通知我們的進(jìn)程也可用回調(diào)函數(shù)來進(jìn)行異步通知,接下來我會(huì)為大家主要介紹以下回調(diào)函數(shù)來進(jìn)行異步通知,關(guān)于信號(hào)通知有興趣的同學(xué)自己去學(xué)習(xí)吧
使用回調(diào)進(jìn)行異步通知
該種通知方式使用一個(gè)系統(tǒng)回調(diào)函數(shù)來通知應(yīng)用程序,要想完成此功能,我們必須在aiocb中設(shè)置我們想要進(jìn)行異步回調(diào)的aiocb指針,以用來回調(diào)之后表示其自身
實(shí)例如下
aio線程回調(diào)通知
- 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
線程會(huì)掉是通過使用aiocb結(jié)構(gòu)體中的aio_sigevent結(jié)構(gòu)體來控制的,
其定義如下
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
總結(jié)
以上是生活随笔為你收集整理的linux下aio异步读写详解与实例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 慢慢聊Linux AIO
- 下一篇: linux AIO (异步IO) 那点事