色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

linux的異步IO分析

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-09 16:50 ? 次閱讀

一、簡介

1.1 POSIX AIO

POSIX AIO是一個用戶級實現,它在多個線程中執行正常的阻塞I/O,因此給出了I/O異步的錯覺.這樣做的主要原因是:

  • 它適用于任何文件系統
  • 它(基本上)在任何操作系統上工作(請記住,gnu的libc是可移植的)
  • 它適用于啟用了緩沖的文件(即沒有設置O_DIRECT標志)

主要缺點是你的隊列深度(即你在實踐中可以擁有的未完成操作的數量)受到你選擇的線程數量的限制,這也意味著一個磁盤上的慢速操作可能會阻止一個操作進入不同的磁盤.它還會影響內核和磁盤調度程序看到的I/O(或多少)。

1.2 libaio

內核AIO(即io_submit()et.al.)是異步I/O操作的內核支持,其中io請求實際上在內核中排隊,按照您擁有的任何磁盤調度程序排序,可能是其中一些被轉發(我們希望將實際的磁盤作為異步操作(使用TCQ或NCQ)。這種方法的主要限制是,并非所有文件系統都能很好地工作,或者根本不能使用異步I/O(并且可能會回到阻塞語義),因此必須使用O_DIRECT打開文件,而O_DIRECT還帶有許多其他限制。。I/O請求。如果您無法使用O_DIRECT打開文件,它可能仍然"正常",就像您獲得正確的數據一樣,但它可能不是異步完成,而是回到阻止語義。

還要記住,在某些情況下,io_submit()實際上可以阻塞磁盤。

沒有aio_*系統調用(http://linux.die.net/man/2/syscalls)。您在vfs中看到的aio_函數可能是內核aio的一部分。用戶級aio_*函數不會將1:1映射到系統調用。

二、POSIX AIO(用戶層級)

2.1 異步IO基本API

圖片

2.2 重要結構體

上述API調用都會用到 struct aiocb 結構體:

1 struct aiocb {
2 int aio_fildes; //文件描述符
3 off_t aio_offset; //文件偏移量
4 volatile void *aio_buf; //緩沖區
5 size_t aio_nbytes; //數據長度
6 int aio_reqprio; //請求優先級
7 struct sigevent aio_sigevent; //通知方式
8 int aio_lio_opcode; //要執行的操作
9 };

2.3 使用時注意事項

編譯時加參數 -lrt

2.4 aio_error

2.4.1 功能

檢查異步請求狀態

2.4.2 函數原型

int aio_error(const struct aiocb *aiocbp);

2.4.3 返回值

圖片

2.5 aio_read

2.5.1 功能

異步讀操作,aio_read函數請求對一個文件進行讀操作,所請求文件對應的文件描述符可以是文件,套接字,甚至管道。

2.5.2 函數原型

int aio_error(const struct aiocb *aiocbp);

2.5.3 返回值

該函數請求對文件進行異步讀操作,若請求失敗返回-1,成功則返回0,并將該請求進行排隊,然后就開始對文件的異步讀操作。需要注意的是,我們得先對aiocb結構體進行必要的初始化。

2.5.4 使用示例

2.5.4.1 代碼

特別提醒在編譯上述程序時必須在編譯時再加一個-lrt,如gcc test.c -o test -lrt。

1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13
14
15 #define BUFFER_SIZE 1024
16
17 int MAX_LIST = 2;
18
19 int main(int argc,char **argv)
20 {
21 //aio操作所需結構體
22 struct aiocb rd;
23
24 int fd,ret,couter;
25
26 fd = open("test.txt",O_RDONLY);
27 if(fd < 0)
28 {
29 perror("test.txt");
30 }
31
32
33
34 //將rd結構體清空
35 bzero(&rd,sizeof(rd));
36
37
38 //為rd.aio_buf分配空間
39 rd.aio_buf = malloc(BUFFER_SIZE + 1);
40
41 //填充rd結構體
42 rd.aio_fildes = fd;
43 rd.aio_nbytes = BUFFER_SIZE;
44 rd.aio_offset = 0;
45
46 //進行異步讀操作
47 ret = aio_read(&rd);
48 if(ret < 0)
49 {
50 perror("aio_read");
51 exit(1);
52 }
53
54 couter = 0;
55 // 循環等待異步讀操作結束
56 while(aio_error(&rd) == EINPROGRESS)
57 {
58 printf("第%d次n",++couter);
59 }
60 //獲取異步讀返回值
61 ret = aio_return(&rd);
62
63 printf("nn返回值為:%d",ret);
64
65
66 return 0;
67 }

2.5.4.2 代碼分析

上述實例中aiocb結構體用來表示某一次特定的讀寫操作,在異步讀操作時我們只需要注意4點內容

  • 1.確定所要讀的文件描述符,并寫入aiocb結構體中(下面幾條一樣不再贅余)
  • 2.確定讀所需的緩沖區
  • 3.確定讀的字節數
  • 4.確定文件的偏移量

總結以上注意事項:基本上和我們的read函數所需的條件相似,唯一的區別就是多一個文件偏移量。

2.5.4.3 執行結果分析

運行結果如下:

圖片

從上圖看出,循環檢查了3次異步讀寫的狀態(不同機器的檢查次數可能不一樣,和具體的計算機性能有關系),指定的256字節才讀取完畢,最后返回讀取的字節數256。

如果注釋掉異步讀的狀態檢查:

1 ...
2
3 //查看異步讀取的狀態,直到讀取請求完成
4 /* for(i = 1;aio_error(&cbp) == EINPROGRESS;i++)
5 {
6 printf("No.%3dn",i);
7 }
8 ret = aio_return(&cbp);
9 printf("return %dn",ret);
10 */
11 ...

此時的運行結果:

圖片

發現什么都沒輸出,這是因為程序結束的時候,異步讀請求還沒完成,所以buf緩沖區還沒有讀進去數據。

如果將上面代碼中的 sleep 的注釋去掉,讓異步請求發起后,程序等待1秒后再輸出,就會發現成功讀取到了數據。

用GDB單步跟蹤上面程序,當發起異步讀請求時:

圖片

看到發起一個異步請求時,Linux實際上是創建了一個線程去處理,當請求完成后結束線程。

2.6 aio_write

2.6.1 功能

aio_writr用來請求異步寫操作

2.6.2 函數原型

int aio_write(struct aiocb *paiocb);

2.6.3 返回值

aio_write和aio_read函數類似,當該函數返回成功時,說明該寫請求以進行排隊(成功0,失敗-1)。

2.6.4 使用示例

2.6.4.1 代碼

特別提醒在編譯上述程序時必須在編譯時再加一個-lrt,如gcc test.c -o test -lrt。

1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13
14 #define BUFFER_SIZE 1025
15
16 int main(int argc,char **argv)
17 {
18 //定義aio控制塊結構體
19 struct aiocb wr;
20
21 int ret,fd;
22
23 char str[20] = {"hello,world"};
24
25 //置零wr結構體
26 bzero(&wr,sizeof(wr));
27
28 fd = open("test.txt",O_WRONLY | O_APPEND);
29 if(fd < 0)
30 {
31 perror("test.txt");
32 }
33
34 //為aio.buf申請空間
35 wr.aio_buf = (char *)malloc(BUFFER_SIZE);
36 if(wr.aio_buf == NULL)
37 {
38 perror("buf");
39 }
40
41 wr.aio_buf = str;
42
43 //填充aiocb結構
44 wr.aio_fildes = fd;
45 wr.aio_nbytes = 1024;
46
47 //異步寫操作
48 ret = aio_write(&wr);
49 if(ret < 0)
50 {
51 perror("aio_write");
52 }
53
54 //等待異步寫完成
55 while(aio_error(&wr) == EINPROGRESS)
56 {
57 printf("hello,worldn");
58 }
59
60 //獲得異步寫的返回值
61 ret = aio_return(&wr);
62 printf("nnn返回值為:%dn",ret);
63
64 return 0;
65 }

2.6.4.2 代碼分析

上面test.txt文件是以追加的方式打開的,所以不需要設置aio_offset 文件偏移量,不管文件偏移的值是多少都會在文件末尾寫入??梢匀サ鬙_APPEND, 不以追加方式打開文件,這樣就可以設置 aio_offset ,指定從何處位置開始寫入文件。

2.6 aio_suspend

2.6.1 功能

aio_suspend函數可以將當前進程掛起,直到有向其注冊的異步事件完成為止。

2.6.2 函數原型

1 int aio_suspend(const struct aiocb * const aiocb_list[],
2 int nitems, const struct timespec *timeout);

2.6.3 返回值

如果在定時時間達到之前,因為完成了IO請求導致函數返回,此時返回值是0;否則,返回值為-1,并且會設置errno。

2.6.4 使用實例

2.6.4.1 代碼

前面2個例子發起IO請求都是非阻塞的,即使IO請求未完成,也不影響調用程序繼續執行后面的語句。但是,我們也可以調用aio_suspend 來阻塞一個或多個異步IO, 只需要將IO請求加入阻塞列表。

1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9
10 #define BUFSIZE 1024
11 #define MAX 2
12
13 //異步讀請求
14 int aio_read_file(struct aiocb *cbp,int fd,int size)
15 {
16 int ret;
17 bzero(cbp,sizeof(struct aiocb));
18
19 cbp->aio_buf = (volatile void*)malloc(size+1);
20 cbp->aio_nbytes = size;
21 cbp->aio_offset = 0;
22 cbp->aio_fildes = fd;
23
24 ret = aio_read(cbp);
25 if(ret < 0)
26 {
27 perror("aio_read errorn");
28 exit(1);
29 }
30 }
31
32 int main()
33 {
34 struct aiocb cbp1,cbp2;
35 int fd1,fd2,ret;
36 int i = 0;
37 //異步阻塞列表
38 struct aiocb* aiocb_list[2];
39
40 fd1 = open("test.txt",O_RDONLY);
41 if(fd1 < 0)
42 {
43 perror("open errorn");
44 }
45 aio_read_file(&cbp1,fd1,BUFSIZE);
46
47 fd2 = open("test.txt",O_RDONLY);
48 if(fd2 < 0)
49 {
50 perror("open errorn");
51 }
52 aio_read_file(&cbp2,fd2,BUFSIZE*4);
53
54 //向列表加入兩個請求
55 aiocb_list[0] = &cbp1;
56 aiocb_list[1] = &cbp2;
57 //阻塞,直到請求完成才會繼續執行后面的語句
58 aio_suspend((const struct aiocb* const*)aiocb_list,MAX,NULL);
59 printf("read1:%sn",(char*)cbp1.aio_buf);
60 printf("read2:%sn",(char*)cbp2.aio_buf);
61
62 close(fd1);
63 close(fd2);
64 return 0;
65 }

運行結果

圖片

可以看到只有read2讀到了數據,因為只要有IO請求完成阻塞函數aio_suspend就會直接就返回用戶線程了,繼續執行下面的語句。

2.7 lio_listio

2.7.1 功能

aio同時還為我們提供了一個可以發起多個或多種I/O請求的接口lio_listio。這個函數效率很高,因為我們只需一次系統調用(一次內核上下位切換)就可以完成大量的I/O操作。第一個參數:LIO_WAIT (阻塞) 或 LIO_NOWAIT(非阻塞),第二個參數:異步IO請求列表

2.7.2 函數原型

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

2.7.3 返回值

如果mode是LIO_NOWAIT,當所有IO請求成功入隊后返回0,否則,返回-1,并設置errno。如果mode是LIO_WAIT,當所有IO請求完成之后返回0,否則,返回-1,并設置errno。

2.7.4 使用示例

2.7.4.1 代碼

1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9
10 #define BUFSIZE 100
11 #define MAX 2
12
13 //異步讀結構體
14 int aio_read_file(struct aiocb *cbp,int fd,int size)
15 {
16 int ret;
17 bzero(cbp,sizeof(struct aiocb));
18
19 cbp->aio_buf = (volatile void*)malloc(size+1);
20 cbp->aio_nbytes = size;
21 cbp->aio_offset = 0;
22 cbp->aio_fildes = fd;
23 cbp->aio_lio_opcode = LIO_READ;
24 }
25
26 int main()
27 {
28 struct aiocb cbp1,cbp2;
29 int fd1,fd2,ret;
30 int i = 0;
31 //異步請求列表
32 struct aiocb* io_list[2];
33
34 fd1 = open("test.txt",O_RDONLY);
35 if(fd1 < 0)
36 {
37 perror("open errorn");
38 }
39 aio_read_file(&cbp1,fd1,BUFSIZE);
40
41 fd2 = open("test.txt",O_RDONLY);
42 if(fd2 < 0)
43 {
44 perror("open errorn");
45 }
46 aio_read_file(&cbp2,fd2,BUFSIZE*4);
47
48 io_list[0] = &cbp1;
49 io_list[1] = &cbp2;
50
51 lio_listio(LIO_WAIT,(struct aiocb* const*)io_list,MAX,NULL);
52 printf("read1:%sn",(char*)cbp1.aio_buf);
53 printf("read2:%sn",(char*)cbp2.aio_buf);
54
55 close(fd1);
56 close(fd2);
57 return 0;
58 }

運行結果:

圖片

2.7 異步IO通知機制

2.7.1 簡介

異步與同步的區別就是我們不需要等待異步操作返回就可以繼續干其他的事情,當異步操作完成時可以通知我們去處理它。

以下兩種方式可以處理異步通知:

2.7.2 信號處理

2.7.2.1 示例

在發起異步請求時,可以指定當異步操作完成時給調用進程發送什么信號,這樣調用收到此信號就會執行相應的信號處理函數。

代碼:

1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10
11 #define BUFSIZE 256
12
13 //信號處理函數,參數signo接收的對應的信號值
14 void aio_handler(int signo)
15 {
16 int ret;
17 printf("異步操作完成,收到通知n");
18 }
19
20 int main()
21 {
22 struct aiocb cbp;
23 int fd,ret;
24 int i = 0;
25
26 fd = open("test.txt",O_RDONLY);
27
28 if(fd < 0)
29 {
30 perror("open errorn");
31 }
32
33 //填充struct aiocb 結構體
34 bzero(&cbp,sizeof(cbp));
35 //指定緩沖區
36 cbp.aio_buf = (volatile void*)malloc(BUFSIZE+1);
37 //請求讀取的字節數
38 cbp.aio_nbytes = BUFSIZE;
39 //文件偏移
40 cbp.aio_offset = 0;
41 //讀取的文件描述符
42 cbp.aio_fildes = fd;
43 //發起讀請求
44
45 //設置異步通知方式
46 //用信號通知
47 cbp.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
48 //發送異步信號
49 cbp.aio_sigevent.sigev_signo = SIGIO;
50 //傳入aiocb 結構體
51 cbp.aio_sigevent.sigev_value.sival_ptr = &cbp;
52
53 //安裝信號
54 signal(SIGIO,aio_handler);
55 //發起異步讀請求
56 ret = aio_read(&cbp);
57 if(ret < 0)
58 {
59 perror("aio_read errorn");
60 exit(1);
61 }
62 //暫停4秒,保證異步請求完成
63 sleep(4);
64 close(fd);
65 return 0;
66 }

運行結果:

圖片

2.7.2.2 小結

SIGIO 是系統專門用來表示異步通知的信號,當然也可以發送其他的信號,比如將上面的SIGIO替換為 SIGRTMIN+1 也是可以的。安裝信號可以使用signal(),但是signal不可以傳遞參數進去。所以推薦使用sigaction()函數,可以為信號處理設置更多的東西。

2.7.3 線程回調

顧名思義就是當調用進程收到異步操作完成的通知時,開線程執行設定好的回調函數去處理。無需專門安裝信號。在Glibc AIO 的實現中, 用多線程同步來模擬 異步IO ,以上述代碼為例,它牽涉了3個線程, 主線程(23908)新建 一個線程(23909)來調用阻塞的pread函數,當pread返回時,又創建了一個線程(23910)來執行我們預設的異步回調函數, 23909 等待23910結束返回,然后23909也結束執行。與信號處理方式相比,如上圖,采用線程回調的方式可以使得在處理異步通知的時候不會阻塞當前調用進程。

實際上,為了避免線程的頻繁創建、銷毀,當有多個請求時,Glibc AIO 會使用線程池,但以上原理是不會變的,尤其要注意的是:我們的回調函數是在一個單獨線程中執行的.

Glibc AIO 廣受非議,存在一些難以忍受的缺陷和bug,飽受詬病,是極不推薦使用的.

1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10
11 #define BUFSIZE 256
12
13 //回調函數
14 void aio_handler(sigval_t sigval)
15 {
16 struct aiocb *cbp;
17 int ret;
18
19 printf("異步操作完成,收到通知n");
20 //獲取aiocb 結構體的信息
21 cbp = (struct aiocb*)sigval.sival_ptr;
22
23 if(aio_error(cbp) == 0)
24 {
25 ret = aio_return(cbp);
26 printf("讀請求返回值:%dn",ret);
27 }
28 while(1)
29 {
30 printf("正在執行回調函數。。。n");
31 sleep(1);
32 }
33 }
34
35 int main()
36 {
37 struct aiocb cbp;
38 int fd,ret;
39 int i = 0;
40
41 fd = open("test.txt",O_RDONLY);
42
43 if(fd < 0)
44 {
45 perror("open errorn");
46 }
47
48 //填充struct aiocb 結構體
49 bzero(&cbp,sizeof(cbp));
50 //指定緩沖區
51 cbp.aio_buf = (volatile void*)malloc(BUFSIZE+1);
52 //請求讀取的字節數
53 cbp.aio_nbytes = BUFSIZE;
54 //文件偏移
55 cbp.aio_offset = 0;
56 //讀取的文件描述符
57 cbp.aio_fildes = fd;
58 //發起讀請求
59
60 //設置異步通知方式
61 //用線程回調
62 cbp.aio_sigevent.sigev_notify = SIGEV_THREAD;
63 //設置回調函數
64 cbp.aio_sigevent.sigev_notify_function = aio_handler;
65 //傳入aiocb 結構體
66 cbp.aio_sigevent.sigev_value.sival_ptr = &cbp;
67 //設置屬性為默認
68 cbp.aio_sigevent.sigev_notify_attributes = NULL;
69
70 //發起異步讀請求
71 ret = aio_read(&cbp);
72 if(ret < 0)
73 {
74 perror("aio_read errorn");
75 exit(1);
76 }
77 //調用進程繼續執行
78 while(1)
79 {
80 printf("主線程繼續執行。。。n");
81 sleep(1);
82 }
83 close(fd);
84 return 0;
85 }

運行結果:

圖片

三、libaio

3.1 簡介

上面介紹的aio其實是用戶層使用線程模擬的異步io,缺點是占用線程資源而且受可用線程的數量限制。Linux2.6版本后有了libaio,這完全是內核級別的異步IO,IO請求完全由底層自由調度(以最佳次序的磁盤調度方式)。

libaio的缺點是,想要使用該種方式的文件必須支持以O_DIRECT標志打開,然而并不是所有的文件系統都支持。如果你沒有使用O_DIRECT打開文件,它可能仍然“工作”,但它可能不是異步完成的,而是變為了阻塞的。

3.2 安裝

sudo apt-get install libaio-dev

3.3 結構體

3.3.1 aiocb

aiocb結構體:

1 struct aiocb
2 {
3 //要異步操作的文件描述符
4 int aio_fildes;
5 //用于lio操作時選擇操作何種異步I/O類型
6 int aio_lio_opcode;
7 //異步讀或寫的緩沖區的緩沖區
8 volatile void *aio_buf;
9 //異步讀或寫的字節數
10 size_t aio_nbytes;
11 //異步通知的結構體
12 struct sigevent aio_sigevent;
13 }

3.3.2 timespec

3.3.2.1 源碼

timespec結構體

1 struct timespec {
2 time_t tv_sec; /* seconds */
3 long tv_nsec; /* nanoseconds [0 .. 999999999] */
4 };

3.3.2.2 成員分析

tv_sec:秒數

tv_nsec:毫秒數

3.3.3 io_event

3.3.3.1 源碼

io_event結構體

1 struct io_event {
2 void *data;
3 struct iocb *obj;
4 unsigned long res;
5 unsigned long res2;
6 };

3.3.3.2 成員分析

io_event是用來描述返回結果的:

obj就是之前提交IO任務時的iocb; res和res2來表示IO任務完成的狀態。

3.3.4 io_iocb_common

3.3.4.1 源碼

io_iocb_common結構體

1 struct io_iocb_common { // most import structure, either io and iov are both initialized based on this
2
3 PADDEDptr(void *buf, __pad1); // pointer to buffer for io, pointer to iov for io vector
4
5 PADDEDul(nbytes, __pad2); // number of bytes in one io, or number of ios for io vector
6
7 long long offset; //disk offset
8
9 long long __pad3;
10
11 unsigned flags; // interface for set_eventfd(), flag to use eventfd
12
13 unsigned resfd;// interface for set_eventfd(), set to eventfd
14
15 }; /* result code is the amount read or -'ve errno */

io_iocb_common是也是異步IO庫里面最重要的數據結構,上面iocb數據結構中的聯合u,通常就是按照io_iocb_common的格式來初始化的。

3.3.4.2 成員分析

成員 buf: 在批量IO的模式下,表示一個iovector 數組的起始地址;在單一IO的模式下,表示數據的起始地址; 成員 nbytes: 在批量IO的模式下,表示一個iovector 數組的元素個數;在單一IO的模式下,表示數據的長度; 成員 offset: 表示磁盤或者文件上IO開始的起始地址(偏移) 成員 _pad3: 填充字段,目前可以忽略 成員 flags: 表示是否實用eventfd 機制 成員 resfd: 如果使用eventfd機制,就設置成之前初始化的eventfd的值, io_set_eventfd()就是用來封裝上面兩個域的。

3.3.5 io_iocb

3.3.5.1 源碼

1 struct iocb {
2 PADDEDptr(void *data, __pad1); /* Return in the io completion event */ /// will passed to its io finished events, channel of callback
3
4 PADDED(unsigned key, __pad2); /* For use in identifying io requests */ /// identifier of which iocb, initialized with iocb address?
5
6 short aio_lio_opcode; // io opration code, R/W SYNC/DSYNC/POOL and so on
7
8 short aio_reqprio; // priority
9
10 int aio_fildes; // aio file descriptor, show which file/device the operation will take on
11
12 union {
13 struct io_iocb_common c; // most important, common initialization interface
14
15 struct io_iocb_vector v;
16
17 struct io_iocb_poll poll;
18
19 struct io_iocb_sockaddr saddr;
20
21 } u;
22
23 };

3.3.5.2 成員分析

iocb是異步IO庫里面最重要的數據結構,大部分異步IO函數都帶著這個參數。

成員 data: 當前iocb對應的IO完成之后,這個data的值會自動傳遞到這個IO生成的event的data這個域上去

成員 key: 標識哪一個iocb,通常沒用

成員 ail_lio_opcode:指示當前IO操作的類型,可以初始化為下面的類型之一:

1 typedef enum io_iocb_cmd { // IO Operation type, used to initialize aio_lio_opcode
2
3 IO_CMD_PREAD = 0,
4
5 IO_CMD_PWRITE = 1,
6
7 IO_CMD_FSYNC = 2,
8
9 IO_CMD_FDSYNC = 3,
10
11 IO_CMD_POLL = 5, /* Never implemented in mainline, see io_prep_poll */
12
13 IO_CMD_NOOP = 6,
14
15 IO_CMD_PREADV = 7,
16
17 IO_CMD_PWRITEV = 8,
18
19 } io_iocb_cmd_t;

成員 aio_reqprio: 異步IO請求的優先級,可能和io queue相關

成員 aio_filds: 接受IO的打開文件描述符,注意返回這個描述符的open()函數所帶的CREATE/RDWR參數需要和上面的opcod相匹配,滿足讀寫權限屬性檢查規則。

成員 u: 指定IO對應的數據起始地址,或者IO向量數組的起始地址

3.4 libaio系統調用

linux kernel 提供了5個系統調用來實現異步IO。文中最后介紹的是包裝了這些系統調用的用戶空間的函數。

1 int io_setup(unsigned nr_events, aio_context_t *ctxp);
2 int io_destroy(aio_context_t ctx);
3 int io_submit(aio_context_t ctx, long nr, struct iocb *cbp[]);
4 int io_cancel(aio_context_t ctx, struct iocb *, struct io_event *result);
5 int io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *timeout);

1、建立IO任務

int io_setup (int maxevents, io_context_t *ctxp);

io_context_t對應內核中一個結構,為異步IO請求提供上下文環境。注意在setup前必須將io_context_t初始化為0。當然,這里也需要open需要操作的文件,注意設置O_DIRECT標志。

2、提交IO任務

long io_submit (aio_context_t ctx_id, long nr, struct iocb **iocbpp);

提交任務之前必須先填充iocb結構體,libaio提供的包裝函數說明了需要完成的工作:

3.獲取完成的IO long io_getevents (aio_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout); 這里最重要的就是提供一個io_event數組給內核來copy完成的IO請求到這里,數組的大小是io_setup時指定的maxevents。timeout是指等待IO完成的超時時間,設置為NULL表示一直等待所有到IO的完成。 4.銷毀IO任務 int io_destrory (io_context_t ctx)

3.5 異步IO上下文 aio_context_t

代碼:

1 #define _GNU_SOURCE /* syscall() is not POSIX */
2 #include /* for perror() */
3 #include /* for syscall() */
4 #include /* for __NR_* definitions */
5 #include /* for AIO types and constants */
6 inline int io_setup(unsigned nr, aio_context_t *ctxp)
7 {
8 return syscall(__NR_io_setup, nr, ctxp);
9 }
10 inline int io_destroy(aio_context_t ctx)
11 {
12 return syscall(__NR_io_destroy, ctx);
13 }
14 int main()
15 {
16 aio_context_t ctx;
17 int ret;
18 ctx = 0;
19 ret = io_setup(128, &ctx);
20 if (ret < 0) {
21 perror("io_setup error");
22 return -1;
23 }
24 printf("after io_setup ctx:%Ldn",ctx);
25 ret = io_destroy(ctx);
26 if (ret < 0) {
27 perror("io_destroy error");
28 return -1;
29 }
30 printf("after io_destroy ctx:%Ldn",ctx);
31 return 0;
32 }

系統調用io_setup會創建一個所謂的"AIO上下文"(即aio_context,后文也叫‘AIO context’等)結構體到在內核中。aio_context是用以內核實現異步AIO的數據結構。它其實是一個無符號整形,位于頭文件 /usr/include/linux/aio_abi.h。

typedef unsigned long aio_context_t;

每個進程都可以有多個aio_context_t。傳入io_setup的第一個參數在這里是128,表示同時駐留在上下文中的IO請求的個數;第二個參數是一個指針,內核會填充這個值。io_destroy的作用是銷毀這個上下文aio_context_t。上面的例子很簡單,創建一個aio_context_t并銷毀。

3.6 提交并查詢IO

流程:

  • 每一個提交的IO請求用結構體struct iocb來表示。
  • 首先初始化這個結構體為全零: memset(&cb, 0, sizeof(cb));
  • 然后初始化文件描述符(cb.aio_fildes = fd)和AIO 命令(cb.aio_lio_opcode = IOCB_CMD_PWRITE)
  • 文件描述符對應上文所打開的文件。本例中是./testfile.

代碼:

1 #define _GNU_SOURCE /* syscall() is not POSIX */
2 #include /* for perror() */
3 #include /* for syscall() */
4 #include /* for __NR_* definitions */
5 #include /* for AIO types and constants */
6 #include /* O_RDWR */
7 #include /* memset() */
8 #include /* uint64_t */
9 inline int io_setup(unsigned nr, aio_context_t *ctxp)
10 {
11 return syscall(__NR_io_setup, nr, ctxp);
12 }
13
14 inline int io_destroy(aio_context_t ctx)
15 {
16 return syscall(__NR_io_destroy, ctx);
17 }
18
19 inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp)
20 {
21 return syscall(__NR_io_submit, ctx, nr, iocbpp);
22 }
23
24 inline int io_getevents(aio_context_t ctx, long min_nr, long max_nr, struct io_event *events, struct timespec *timeout)
25 {
26 return syscall(__NR_io_getevents, ctx, min_nr, max_nr, events, timeout);
27 }
28
29 int main()
30 {
31 aio_context_t ctx;
32 struct iocb cb; struct iocb *cbs[1];
33 char data[4096];
34 struct io_event events[1];
35 int ret;
36 int fd;
37 int i ;
38 for(i=0;i<4096;i++)
39 {
40 data[i]=i%50+60;
41 }
42 fd = open("./testfile", O_RDWR | O_CREAT,S_IRWXU);
43 if (fd < 0)
44 {
45 perror("open error");
46 return -1;
47 }
48
49 ctx = 0;
50 ret = io_setup(128, &ctx);
51 printf("after io_setup ctx:%ld",ctx);
52 if (ret < 0)
53 {
54 perror("io_setup error");
55 return -1;
56 } /* setup I/O control block */
57 memset(&cb, 0, sizeof(cb));
58 cb.aio_fildes = fd;
59 cb.aio_lio_opcode = IOCB_CMD_PWRITE;/* command-specific options */
60 cb.aio_buf = (uint64_t)data;
61 cb.aio_offset = 0;
62 cb.aio_nbytes = 4096;
63 cbs[0] = &cb;
64 ret = io_submit(ctx, 1, cbs);
65 if (ret != 1)
66 {
67 if (ret < 0)
68 perror("io_submit error");
69 else
70 fprintf(stderr, "could not sumbit IOs");
71 return -1;
72 } /* get the reply */
73
74 ret = io_getevents(ctx, 1, 1, events, NULL);
75 printf("%dn", ret);
76 struct iocb * result = (struct iocb *)events[0].obj;
77 printf("reusult:%Ld",result->aio_buf);
78 ret = io_destroy(ctx);
79 if (ret < 0)
80 {
81 perror("io_destroy error");
82 return -1;
83 }
84 return 0;
85 }

3.7 內核當前支持的AIO 命令

內核當前支持的AIO 命令有

1 IOCB_CMD_PREAD 讀; 對應系統調用pread().
2 IOCB_CMD_PWRITE 寫,對應系統調用pwrite().
3 IOCB_CMD_FSYNC 同步文件數據到磁盤,對應系統調用fsync()
4 IOCB_CMD_FDSYNC 同步文件數據到磁盤,對應系統調用fdatasync()
5 IOCB_CMD_PREADV 讀,對應系統調用readv()
6 IOCB_CMD_PWRITEV 寫,對應系統調用writev()
7 IOCB_CMD_NOOP 只是內核使用

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • IO
    IO
    +關注

    關注

    0

    文章

    451

    瀏覽量

    39222
  • Linux
    +關注

    關注

    87

    文章

    11322

    瀏覽量

    209890
  • 操作系統
    +關注

    關注

    37

    文章

    6856

    瀏覽量

    123460
  • 磁盤
    +關注

    關注

    1

    文章

    379

    瀏覽量

    25227
收藏 人收藏

    評論

    相關推薦

    Linux驅動學習筆記:異步IO

    前幾篇介紹了幾種IO模型,今天介紹另一種IO模型——異步IO。
    發表于 06-12 16:24 ?719次閱讀

    Linux設備驅動中的異步通知與異步I/O

    ;信號驅動的異步I/O"。Linux信號Linux系統中,異步通知使用信號來實現。信號也就是一種軟件中斷。信號的產生:kill raise alarm用戶按下某些終端鍵;硬件異常;終止
    發表于 02-21 10:52

    linux下的IO模型詳解

      開門見山,Linux下的如中IO模型:阻塞IO模型,非阻塞IO模型,IO復用模型,信號驅動IO
    發表于 10-09 16:12

    異步IO是什么

    python 異步ioAsync IO is a concurrent programming design that has received dedicated support
    發表于 09-06 07:26

    Linux設備驅動開發詳解》第9章、Linux設備驅動中的異步通知與異步IO

    Linux設備驅動開發詳解》第9章、Linux設備驅動中的異步通知與異步IO
    發表于 10-27 11:33 ?0次下載
    《<b class='flag-5'>Linux</b>設備驅動開發詳解》第9章、<b class='flag-5'>Linux</b>設備驅動中的<b class='flag-5'>異步</b>通知與<b class='flag-5'>異步</b><b class='flag-5'>IO</b>

    Linux設備驅動開發詳解》第8章、Linux設備驅動中的阻塞與非阻塞IO

    Linux設備驅動開發詳解》第8章、Linux設備驅動中的阻塞與非阻塞IO
    發表于 10-27 11:35 ?9次下載
    《<b class='flag-5'>Linux</b>設備驅動開發詳解》第8章、<b class='flag-5'>Linux</b>設備驅動中的阻塞與非阻塞<b class='flag-5'>IO</b>

    Python異步IO分析

    異步操作在計算機軟硬件體系中是一個普遍概念,根源在于參與協作的各實體處理速度上有明顯差異。
    的頭像 發表于 02-02 09:04 ?1530次閱讀

    Linux驅動技術中的異步通知技術

    異步通知的全稱是"信號驅動的異步IO",通過"信號"的方式,期望獲取的資源可用時,驅動會主動通知指定的應用程序,和應用層的"信號"相對應,這里使用的是信號"SIGIO"。
    發表于 05-12 09:24 ?683次閱讀
    <b class='flag-5'>Linux</b>驅動技術中的<b class='flag-5'>異步</b>通知技術

    嵌入式Linux 異步IO機制

    嵌入式linux中文站向廣大嵌入式linux愛好者介紹一下嵌入式linux系統的I/O機制。Linux的I/O機制經歷了一下幾個階段的演進:1. 同步阻塞I/O: 用戶
    發表于 04-02 14:31 ?362次閱讀

    鴻蒙系統 IO棧和Linux IO棧對比分析

    華為的鴻蒙系統開源之后第一個想看的模塊就是 FS 模塊,想了解一下它的 IO 路徑與 linux 的區別。現在鴻蒙開源的倉庫中有兩個內核系統,一個是 liteos_a 系統,一個是 liteos_m
    的頭像 發表于 10-16 10:45 ?2229次閱讀
    鴻蒙系統 <b class='flag-5'>IO</b>棧和<b class='flag-5'>Linux</b> <b class='flag-5'>IO</b>棧對比<b class='flag-5'>分析</b>

    io_uring 優化 nginx,基于通用應用 nginx 的實戰

    引言 io_uring是Linux內核在v5.1引入的一套異步IO接口,隨著其迅速發展,現在的io_uring已經遠遠超過了純
    的頭像 發表于 10-10 16:19 ?3095次閱讀
    <b class='flag-5'>io</b>_uring 優化 nginx,基于通用應用 nginx 的實戰

    信號驅動IO異步IO的區別

    一. 談信號驅動IO (對比異步IO來看) 信號驅動IO 對比 異步 IO進行理解 信號驅動
    的頭像 發表于 11-08 15:32 ?1092次閱讀
    信號驅動<b class='flag-5'>IO</b>與<b class='flag-5'>異步</b><b class='flag-5'>IO</b>的區別

    linux異步io框架iouring應用

    Linux內核5.1支持了新的異步IO框架iouring,由Block IO大神也即Fio作者Jens Axboe開發,意在提供一套公用的網絡和磁盤
    的頭像 發表于 11-08 15:39 ?700次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>異步</b><b class='flag-5'>io</b>框架iouring應用

    異步IO框架iouring介紹

    前言 Linux內核5.1支持了新的異步IO框架iouring,由Block IO大神也即Fio作者Jens Axboe開發,意在提供一套公用的網絡和磁盤
    的頭像 發表于 11-09 09:30 ?2498次閱讀
    <b class='flag-5'>異步</b><b class='flag-5'>IO</b>框架iouring介紹

    一文解讀Linux 5種IO模型

    Linux里有五種IO模型:阻塞IO、非阻塞IO、多路復用IO、信號驅動式IO
    的頭像 發表于 11-09 11:12 ?363次閱讀
    一文解讀<b class='flag-5'>Linux</b> 5種<b class='flag-5'>IO</b>模型
    主站蜘蛛池模板: 717影院理论午夜伦不卡久久| 999www成人免费视频| ebc5恐怖5a26房间| 美美哒免费影视8| 521人成a天堂v| 柠檬福利精品视频导航| 99久久国内精品成人免费| 内射人妻无码色AV麻豆去百度搜| 99热最新在线| 起碰免费公开97在线视频| 白丝美女被狂躁免费漫画| 日韩欧美中文字幕一区| 国产成在线观看免费视频| 亚洲 欧美 制服 校园 动漫| 精品国产5g影院天天爽| 10分钟免费观看视频| 欧美九十老太另类| 国产成人精品久久一区二区三区| 亚洲AV久久婷婷蜜臀无码不卡| 久久99热这里只频精品6| 97超碰97资源在线观看| 日韩欧无码一区二区三区免费不卡 | 99re1久久热在线播放| 欧美区一区二| 国产精品www视频免费看| 亚洲视频欧美视频| 欧美ⅹxxxx18性欧美| 成人在线小视频| 亚洲国产日韩欧美在线a乱码| 久久综合久久伊人| 成人在线免费视频播放| 亚洲精品乱码久久久久久中文字幕| 久久精品国产视频澳门| 被肉日常np高h| 亚洲精品91| 欧美一级久久久久久久大| 国产午夜精品理论片影院| 91久久综合精品国产丝袜长腿| 色欲久久精品AV无码| 久久久无码精品亚洲日韩按摩| www黄色com|