Linux內(nèi)核開(kāi)發(fā)之異步通知與異步I/O(三)
小王,聽(tīng)說(shuō)過(guò)AIO沒(méi)?外國(guó)人,就這樣,總是愛(ài)簡(jiǎn)寫,簡(jiǎn)寫的結(jié)果是咱們都不認(rèn)識(shí)了。所謂AIO就是Asynchronous Input/Output異步輸入/輸出,基本思想是允許進(jìn)程發(fā)起很多的I/O操作,而不用阻塞或等待任何操作的完成,稍后或在接收到I/O操作完成的通知時(shí),進(jìn)程就可以檢索I/O操作的結(jié)果。
本文引用地址:http://butianyuan.cn/article/201612/341754.htm“得得,你咋又跟我上起課來(lái)了呢,不是說(shuō)好,今天CS嗎?是不是跟我講課特自信啊“小王抱怨到。
“啊?不是吧,為啥這樣呢,那你到底聽(tīng)不聽(tīng),別到時(shí)面試叫天天不靈叫地地不應(yīng)的,別再找我哈”我氣憤的說(shuō)。
"唉,你都這樣說(shuō)了,我也只能豎起耳朵好好聽(tīng)聽(tīng)了"看著小王極不情愿的表情,我也覺(jué)得很可憐啊。
在異步非阻塞IO中,我們是可以同時(shí)發(fā)起多個(gè)傳輸操作。這需要每個(gè)操作都有一個(gè)唯一的上下文,這樣才能在它們完成時(shí)區(qū)分到底是哪個(gè)傳輸操作完成了。在AIO中,
通過(guò)aiocb(AIO IO control Block)結(jié)構(gòu)體進(jìn)行區(qū)分,這個(gè)結(jié)構(gòu)體如下:
struct aiocb {
int aio_fildes; /* File descriptor */
off_t aio_offset; /* File offset */
volatile void * aio_buf; /* Location of buffer */
size_t aio_nbytes; /* Length of transfer */
int aio_reqprio; /* Request priority offset */
struct sigevent aio_sigevent; /* Signal number and value */
int aio_lio_opcode; /* Operation to be performed */
};
從上邊我們可以看到,這個(gè)結(jié)構(gòu)體包含了有關(guān)傳輸?shù)乃行畔?,包括?shù)據(jù)準(zhǔn)備的用戶緩沖區(qū)。在產(chǎn)生IO通知時(shí),aiocb結(jié)構(gòu)就被用來(lái)唯一標(biāo)識(shí)所完成的IO操作。
AIO系列API中主要有下邊幾個(gè)函數(shù):
1.int aio_read(struct aiocb *aiocbp)
該函數(shù)請(qǐng)求對(duì)一個(gè)有效的文件描述符進(jìn)行異步讀操作。在請(qǐng)求進(jìn)行排隊(duì)之后會(huì)立即返回,如果執(zhí)行成功,返回值就為0,錯(cuò)誤則返回-1并設(shè)置errno的值。
2.int aio_write(struct aiocb *aiocbp)
該函數(shù)請(qǐng)求一個(gè)異步寫操作,它會(huì)立即返回說(shuō)明請(qǐng)求已經(jīng)進(jìn)行排隊(duì),成功返回0,失敗返回為-1,并設(shè)置相應(yīng)的error值。
3.int aio_error(struct aiocb *aiocbp)
該函數(shù)用來(lái)確定請(qǐng)求的狀態(tài),可以返回EINPROGRESS(說(shuō)明請(qǐng)求尚未完成),ECANCELLED(請(qǐng)求被應(yīng)用程序取消了),-1(說(shuō)明發(fā)生了錯(cuò)誤,具體錯(cuò)誤原因由error記錄)。
4.ssize_t aio_return(struct aiocb *aiocbp)
由于并沒(méi)有阻塞在read調(diào)用上,所以我們不能立即返回這個(gè)函數(shù)的返回狀態(tài),這是就要使用這個(gè)函數(shù)了,需要注意的是只有在aio_error調(diào)用確定請(qǐng)求已經(jīng)完成(可能
已經(jīng)完成,也可能發(fā)生了錯(cuò)誤)之后,才能調(diào)用這個(gè)函數(shù),這個(gè)函數(shù)的返回值就相當(dāng)于同步情況下read或write系統(tǒng)調(diào)用的返回值(所傳輸?shù)淖止?jié)數(shù),如果發(fā)生錯(cuò)誤,則返回-1)。
5.int aio_suspend(const struct aiocb *const cblist[], int n ,const struct timespec *timeout)
用戶可以通過(guò)這個(gè)函數(shù)來(lái)來(lái)掛起(或阻塞)調(diào)用進(jìn)程,直到異步請(qǐng)求完成為止,此時(shí)會(huì)產(chǎn)生一個(gè)信號(hào),或者發(fā)生其他超時(shí)操作。調(diào)用者提供了一個(gè)aiocb引用列表,其中任何一個(gè)完成都會(huì)導(dǎo)致給函數(shù)返回。
6.int aio_cancel(int fd ,struct aiocb *aiocbp)
該函數(shù)允許用戶取消對(duì)某個(gè)文件描述符執(zhí)行的一個(gè)或所有的IO請(qǐng)求。
如果要取消一個(gè)請(qǐng)求,用戶需提供文件描述符和aiocb引用,如果這個(gè)請(qǐng)求被成功取消了,則返回AIO_CANCELED,如果該請(qǐng)求完成了,返回AIO_NOTCANCELED.
如果要取消對(duì)某個(gè)給定文件描述符的所有請(qǐng)求,用戶需要提供這個(gè)文件的描述符以及一個(gè)aiocbp的NULL引用,如果所有請(qǐng)求被成功取消了,則返回AIO_CANCELED
,只要至少有一個(gè)沒(méi)被取消,這個(gè)函數(shù)就返回AIO_NOT_CANCELED.如果沒(méi)有一個(gè)請(qǐng)求可以被取消,該函數(shù)就會(huì)返回AIO_ALLDONE.
然后,可以使用aio_error來(lái)驗(yàn)證每個(gè)AIO請(qǐng)求,如果某個(gè)請(qǐng)求已經(jīng)被返回了,那么aio_error就返回-1,并且error會(huì)被設(shè)置為ECANCELED.
7.int lio_listio(int mode ,struct aiocb *list[], int nent ,struct sigevent *sig)
這個(gè)操作使得用戶可以在一個(gè)系統(tǒng)調(diào)用(一次內(nèi)核上下文切換中啟動(dòng)大量的I/O操作)。其中,mode參數(shù)可以是LIO_WAIT或LIO_NOWAIT,前者會(huì)阻塞這個(gè)調(diào)用,直到所有的IO都完成為止,在操作進(jìn)行排隊(duì)之后,LIO_NOWAIT就會(huì)返回,list是一個(gè)aiocb引用的列表,最大元素的個(gè)數(shù)有nent定義的。如果list的元素為NULL,lio_lis
tio()將被忽略。
光說(shuō)理論也不行,是不?現(xiàn)在來(lái)點(diǎn)實(shí)際點(diǎn)的:
a)用戶空間讀例程:
#include
..
int fd, set;
struct aiocb my_aiocb;
fd = open("file.txt", O_RDONLY);
if( fd <0 )
{
perror("open");
}
//清零aiocb結(jié)構(gòu)體
bzero((char *) &my_aiocb, sizeof(struct aiocb));
//為aiocb請(qǐng)求分配數(shù)據(jù)緩沖區(qū)
my_aiocb.aio_buf = malloc(BUFSIZE + 1);
if(!my_aiocb.aio_buf)
perror("malloc");
//初始化aiocb的成員
my_aiocb.aio_fildes = fd;
my_aiocb.aio_nbytes = BUFSIZE;
my_aiocb.aio_offset = 0;
ret = aio_read(&my_aiocb);
if(ret < 0)
perror("aio_read");
while(aio_error(&my_aiocb) == EINPROGRESS)
;
if((ret = aio_return(&my_iocb)))
{
// 獲得異步讀的返回值
}
else
{
讀失敗,分析errror
}
b)用戶空間異步IO aio_suspend()函數(shù)使用例程
struct aioct *cblist(MAX_LIST)
//清零aioct結(jié)構(gòu)鏈表
bzero((char *)cblist, sizeof(cblist));
//將一個(gè)或更多的aiocb放入aioct結(jié)構(gòu)體鏈表
cblist[0] = &my_aiocb;
ret = aio_read( &my_aiocb);
ret = aio_suspend( cblist, MAX_LIST, NULL);
c)用戶空間異步IO lio_list()函數(shù)使用例程
struct aiocb aiocb1,aiocb2;
struct aiocb *list[MAX_LIST];
...
//準(zhǔn)備第一個(gè)aiocb
aiocb1.aio_fildes = fd;
aiocb1.aio_buf = malloc(BUFSIZE +1);
aiocb1.aio_nbytes = BUFSIZE;
aiocb1.aio_offset = next_offset;
aiocb1.aio_lio_opcode = LIO_READ;//異步讀操作
...//準(zhǔn)備多個(gè)aiocb
bzero((char *)list, sizeof(list));
//將aiocb填入鏈表
list[0] = &aiocb1;
list[1] = &aiocb2;
...
ret = lio_listio(LIO_WAIT, list, MAX_LIST, NULL); //發(fā)起大量IO操作
“濤哥,你說(shuō)了這么多,好像也咋沒(méi)和你說(shuō)的驅(qū)動(dòng)扯上關(guān)系呢”小王抱怨道。
“小王,不要急嗎,我不是正打算說(shuō)嗎,瞧你著急性子,這樣吧,你把今天的好好看看,我們下集再說(shuō)”…
評(píng)論