最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

帶你全面了解Linux原生異步 IO 原理與使用和 AIO 實現(xiàn)(Native AIO)(超級詳細)

2022-04-18 18:45 作者:補給站Linux內(nèi)核  | 我要投稿

什么是異步 IO?

異步 IO:當應(yīng)用程序發(fā)起一個 IO 操作后,調(diào)用者不能立刻得到結(jié)果,而是在內(nèi)核完成 IO 操作后,通過信號或回調(diào)來通知調(diào)用者。

異步 IO 與同步 IO 的區(qū)別如 圖1 所示:


  • 從上圖可知,同步 IO 必須等待內(nèi)核把 IO 操作處理完成后才返回。而異步 IO 不必等待 IO 操作完成,而是向內(nèi)核發(fā)起一個 IO 操作就立刻返回,當內(nèi)核完成 IO 操作后,會通過信號的方式通知應(yīng)用程序。

Linux 原生 AIO 原理

  • Linux Native AIO 是 Linux 支持的原生 AIO,為什么要加原生這個詞呢?因為Linux存在很多第三方的異步 IO 庫,如 libeio 和 glibc AIO。所以為了加以區(qū)別,Linux 的內(nèi)核提供的異步 IO 就稱為原生異步 IO。

  • 很多第三方的異步 IO 庫都不是真正的異步 IO,而是使用多線程來模擬異步 IO,如 libeio 就是使用多線程來模擬異步 IO 的。


  • 本文主要介紹 Linux 原生 AIO 的原理和使用,所以不會對其他第三方的異步 IO 庫進行分析,下面我們先來介紹 Linux 原生 AIO 的原理。

  • 如 圖2 所示:

  • Linux 原生 AIO 處理流程:

  1. 當應(yīng)用程序調(diào)用 io_submit 系統(tǒng)調(diào)用發(fā)起一個異步 IO 操作后,會向內(nèi)核的 IO 任務(wù)隊列中添加一個 IO 任務(wù),并且返回成功。

  2. 內(nèi)核會在后臺處理 IO 任務(wù)隊列中的 IO 任務(wù),然后把處理結(jié)果存儲在 IO 任務(wù)中。

  3. 應(yīng)用程序可以調(diào)用 io_getevents 系統(tǒng)調(diào)用來獲取異步 IO 的處理結(jié)果,如果 IO 操作還沒完成,那么返回失敗信息,否則會返回 IO 處理結(jié)果。

  • 從上面的流程可以看出,Linux 的異步 IO 操作主要由兩個步驟組成:

  1. 調(diào)用 io_submit 函數(shù)發(fā)起一個異步 IO 操作。

  2. 調(diào)用 io_getevents 函數(shù)獲取異步 IO 的結(jié)果。

  • 下面我們主要分析,Linux 內(nèi)核是怎么實現(xiàn)異步 IO 的。


【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。∏?00名進群領(lǐng)取,額外贈送一份價值699的內(nèi)核資料包(含視頻教程、電子書、實戰(zhàn)項目及代碼)?


  • 在介紹 Linux 原生 AIO 的實現(xiàn)之前,先通過一個簡單的例子來介紹其使用過程:

  • 上面通過一個簡單的例子來展示了 Linux 原生 AIO 的使用過程,主要有以下步驟:

  1. 通過調(diào)用 open 系統(tǒng)調(diào)用打開要進行異步 IO 的文件,要注意的是 AIO 操作必須設(shè)置 O_DIRECT 直接 IO 標志位。

  2. 調(diào)用 io_setup 系統(tǒng)調(diào)用創(chuàng)建一個異步 IO 上下文。

  3. 調(diào)用 io_prep_pwrite 或者 io_prep_pread 函數(shù)創(chuàng)建一個異步寫或者異步讀任務(wù)。

  4. 調(diào)用 io_submit 系統(tǒng)調(diào)用把異步 IO 任務(wù)提交到內(nèi)核。

  5. 調(diào)用 io_getevents 系統(tǒng)調(diào)用獲取異步 IO 的結(jié)果。

  • 在上面的例子中,我們獲取異步 IO 操作的結(jié)果是在一個無限循環(huán)中進行的,其實 Linux 還支持一種基于 eventfd 事件通知的機制,可以通過 eventfd 和 epoll 結(jié)合來實現(xiàn)事件驅(qū)動的方式來獲取異步 IO 操作的結(jié)果,有興趣可以查閱相關(guān)的內(nèi)容。


  • 上面主要介紹了 Linux 原生 AIO 的原理和使用,Linux 原生 AIO 的使用比較簡單,但其內(nèi)部實現(xiàn)比較復雜,下面介紹 Linux 原生 AIO 的實現(xiàn)過程.

本文基于 Linux-2.6.0 版本內(nèi)核源碼

  • 一般來說,使用 Linux 原生 AIO 需要 3 個步驟:

  1. 調(diào)用 io_setup 函數(shù)創(chuàng)建一個一般 IO 上下文。

  2. 調(diào)用 io_submit 函數(shù)向內(nèi)核提交一個異步 IO 操作。

  3. 調(diào)用 io_getevents 函數(shù)獲取異步 IO 操作結(jié)果。

  • 所以,我們可以通過分析這三個函數(shù)的實現(xiàn)來理解 Linux 原生 AIO 的實現(xiàn)。

Linux 原生 AIO 實現(xiàn)在源碼文件 /fs/aio.c 中。

創(chuàng)建異步 IO 上下文

  • 要使用 Linux 原生 AIO,首先需要創(chuàng)建一個異步 IO 上下文,在內(nèi)核中,異步 IO 上下文使用 kioctx 結(jié)構(gòu)表示,定義如下:

  • 在 kioctx 結(jié)構(gòu)中,比較重要的成員為 active_reqs 和 ring_info。active_reqs 保存了所有正在進行的異步 IO 操作,而 ring_info 成員用于存放異步 IO 操作的結(jié)果。


  • kioctx 結(jié)構(gòu)如 圖1 所示:

  • 如 圖1 所示,active_reqs 成員保存的異步 IO 操作隊列是以 kiocb 結(jié)構(gòu)為單元的,而 ring_info 成員指向一個類型為 aio_ring_info 結(jié)構(gòu)的環(huán)形緩沖區(qū)(Ring Buffer)。

  • 所以我們先來看看 kiocb 結(jié)構(gòu)和 aio_ring_info 結(jié)構(gòu)的定義:

  • kiocb 結(jié)構(gòu)比較簡單,主要用于保存異步 IO 操作的一些信息,如:

  • ki_filp:用于保存進行異步 IO 的文件對象。

  • ki_ctx:指向所屬的異步 IO 上下文對象。

  • ki_list:用于連接當前異步 IO 上下文中的所有 IO 操作對象。

  • ki_user_data:這個字段主要提供給用戶自定義使用,比如區(qū)分異步 IO 操作,或者設(shè)置一個回調(diào)函數(shù)等。

  • ki_pos:用于保存異步 IO 操作的文件偏移量。

  • 而 aio_ring_info 結(jié)構(gòu)是一個環(huán)形緩沖區(qū)的實現(xiàn),其定義如下:

  • 這個環(huán)形緩沖區(qū)主要用于保存已經(jīng)完成的異步 IO 操作的結(jié)果,異步 IO 操作的結(jié)果使用 io_event 結(jié)構(gòu)表示。如 圖2 所示:

  • 圖2 中的 head 代表環(huán)形緩沖區(qū)的開始位置,而 tail 代表環(huán)形緩沖區(qū)的結(jié)束位置,如果 tail 大于 head,則表示有完成的異步 IO 操作結(jié)果可以獲取。如果 head 等于 tail,則表示沒有完成的異步 IO 操作。

  • 環(huán)形緩沖區(qū)的 head 和 tail 位置保存在 aio_ring 的結(jié)構(gòu)中,其定義如下:

  • 上面介紹了那么多數(shù)據(jù)結(jié)構(gòu),只是為了接下來的源碼分析更加容易明白。


  • 現(xiàn)在,我們開始分析異步 IO 上下文的創(chuàng)建過程,異步 IO 上下文的創(chuàng)建通過調(diào)用 io_setup 函數(shù)完成,而 io_setup 函數(shù)會調(diào)用內(nèi)核函數(shù) sys_io_setup,其實現(xiàn)如下:

  • sys_io_setup 函數(shù)的實現(xiàn)比較簡單,首先調(diào)用 ioctx_alloc 申請一個異步 IO 上下文對象,然后把異步 IO 上下文對象的標識符返回給調(diào)用者。


  • 所以,sys_io_setup 函數(shù)的核心過程是調(diào)用 ioctx_alloc ?函數(shù),我們繼續(xù)分析 ioctx_alloc 函數(shù)的實現(xiàn):

  • ioctx_alloc ?函數(shù)主要完成以下工作:

  • 調(diào)用 kmem_cache_alloc 函數(shù)向內(nèi)核申請一個異步 IO 上下文對象。

  • 初始化異步 IO 上下文各個成員變量,如初始化異步 IO 操作隊列。

  • 調(diào)用 aio_setup_ring 函數(shù)初始化環(huán)形緩沖區(qū)。

  • 環(huán)形緩沖區(qū)初始化函數(shù) aio_setup_ring 的實現(xiàn)有點小復雜,主要涉及內(nèi)存管理的知識點,所以這里跳過這部分的分析,有興趣的可以私聊我一起討論。

  • 提交異步 IO 操作

  • 提交異步 IO 操作是通過 io_submit 函數(shù)完成的,io_submit 需要提供一個類型為 iocb 結(jié)構(gòu)的數(shù)組,表示要進行的異步 IO 操作相關(guān)的信息,我們先來看看 iocb 結(jié)構(gòu)的定義:

io_submit 函數(shù)最終會調(diào)用內(nèi)核函數(shù) sys_io_submit 來實現(xiàn)提供異步 IO 操作,我們來分析 sys_io_submit 函數(shù)的實現(xiàn):

sys_io_submit 函數(shù)的實現(xiàn)比較簡單,主要從用戶空間復制異步 IO 操作信息到內(nèi)核空間,然后調(diào)用 io_submit_one 函數(shù)提交異步 IO 操作。我們重點分析 io_submit_one 函數(shù)的實現(xiàn):

  • 上面代碼已經(jīng)對 io_submit_one 函數(shù)進行了詳細的注釋,這里總結(jié)一下 io_submit_one 函數(shù)主要完成的工作:

  1. 通過調(diào)用 fget 函數(shù)獲取文件句柄對應(yīng)的文件對象。

  2. 調(diào)用 aio_get_req 函數(shù)獲取一個類型為 kiocb 結(jié)構(gòu)的異步 IO 操作對象,這個結(jié)構(gòu)前面已經(jīng)分析過。另外,aio_get_req 函數(shù)還會把異步 IO 操作對象添加到異步 IO 上下文的 active_reqs 隊列中。

  3. 根據(jù)不同的異步 IO 操作類型來進行不同的處理,如 異步讀操作 會調(diào)用文件對象的 aio_read 方法來進行處理。不同的文件系統(tǒng),其 aio_read 方法的實現(xiàn)不一樣,如 Ext3 文件系統(tǒng)的 aio_read 方法會指向 generic_file_aio_read 函數(shù)。

  4. 如果異步 IO 操作被添加到內(nèi)核的 IO 請求隊列中,那么就直接返回。否則就代表 IO 操作已經(jīng)完成,那么就調(diào)用 aio_complete 函數(shù)完成收尾工作。

  • io_submit_one 函數(shù)的操作過程如 圖3 所示:

  • 所以,io_submit_one 函數(shù)的主要任務(wù)就是向內(nèi)核提交 IO 請求。

異步 IO 操作完成

  • 當異步 IO 操作完成后,內(nèi)核會調(diào)用 aio_complete 函數(shù)來把處理結(jié)果放進異步 IO 上下文的環(huán)形緩沖區(qū) ring_info 中,我們來分析一下 aio_complete 函數(shù)的實現(xiàn):

  • aio_complete 函數(shù)的 iocb 參數(shù)是我們通過調(diào)用 io_submit_once 函數(shù)提交的異步 IO 對象,而參數(shù) res 和 res2 是用內(nèi)核進行 IO 操作完成后返回的結(jié)果。

  • aio_complete 函數(shù)的主要工作如下:

  • 根據(jù)環(huán)形緩沖區(qū)的 tail 指針獲取一個空閑的 io_event 對象來保存 IO 操作的結(jié)果。

  • 對環(huán)形緩沖區(qū)的 tail 指針進行加一操作,指向下一個空閑的位置。

  • 當把異步 IO 操作的結(jié)果保存到環(huán)形緩沖區(qū)后,用戶層就可以通過調(diào)用 io_getevents 函數(shù)來讀取 IO 操作的結(jié)果,io_getevents 函數(shù)最終會調(diào)用 sys_io_getevents 函數(shù)。


  • 我們來分析 sys_io_getevents 函數(shù)的實現(xiàn):

  • 從上面的代碼可以看出,sys_io_getevents 函數(shù)主要調(diào)用 read_events 函數(shù)來讀取異步 IO 操作的結(jié)果,我們接著分析 read_events 函數(shù):

  • read_events 函數(shù)主要還是調(diào)用 aio_read_evt 函數(shù)來從環(huán)形緩沖區(qū)中讀取異步 IO 操作的結(jié)果,如果讀取成功,就把結(jié)果復制到用戶空間中。

  • aio_read_evt 函數(shù)是從環(huán)形緩沖區(qū)中讀取異步 IO 操作的結(jié)果,其實現(xiàn)如下:

  • aio_read_evt 函數(shù)的主要工作就是判斷環(huán)形緩沖區(qū)是否為空,如果不為空就從環(huán)形緩沖區(qū)中讀取異步 IO 操作的結(jié)果,并且保存到參數(shù) ent 中,并且移動環(huán)形緩沖區(qū)的 head 指針到下一個位置。


帶你全面了解Linux原生異步 IO 原理與使用和 AIO 實現(xiàn)(Native AIO)(超級詳細)的評論 (共 條)

分享到微博請遵守國家法律
新源县| 洞头县| 平邑县| 高青县| 北碚区| 姚安县| 永昌县| 河南省| 梁平县| 彝良县| 彭阳县| 和静县| 玉门市| 堆龙德庆县| 延吉市| 慈溪市| 伊川县| 武城县| 颍上县| 盘山县| 隆化县| 伊通| 文山县| 贵州省| 翁源县| 东乡县| 葫芦岛市| 大城县| 巧家县| 和田县| 万宁市| 青神县| 赣州市| 六安市| 轮台县| 西华县| 津南区| 郸城县| 静海县| 内黄县| 奎屯市|