淺析 Linux 文件 IO 讀寫

淺析 Linux 文件 IO 讀寫

Linux的文件IO子系統(tǒng)是Linux中最復(fù)雜的一個子系統(tǒng)(沒有之一)。讀者可以參考以下這個圖:

https://www.thomas-krenn.com/de/wikiDE/images/2/2d/Linux-storage-stack-diagram_v4.0.pdf

Block Layer的運行調(diào)度模型

數(shù)據(jù)從 Page Cache 同步到磁盤上,發(fā)出的請求稱為一個request,一個request包含一組 bio,每個bio包含要同步的數(shù)據(jù)pages,你要把Page和磁盤的數(shù)據(jù)進行同步。

和網(wǎng)絡(luò)子系統(tǒng)不同,磁盤的調(diào)度是有要求的,不是說你發(fā)一個page,我就幫你寫進去,你再發(fā)一個page,我就給你再寫一個進去。你要寫磁盤的一個地方,磁盤要先把磁頭物理上移動到那個軌道上,然后才能寫,你讓磁頭這樣移來移去的,磁盤的性能就很難看了。

Linux的IO調(diào)度器稱為evelator(電梯),因為Linus開始實現(xiàn)這個系統(tǒng)的時候,使用的就是電梯算法。

坐過電梯很容易理解什么是電梯算法,電梯的算法是:電梯總是從一個方向,把人送到有需要的最高的位置,然后反過來,把人送到有需要的最低一個位置。這樣效率是最高的,因為電梯不用根據(jù)先后順序,不斷調(diào)整方向,走更多的冤枉路。

為了實現(xiàn)這個算法,我們需要一個plug的概念。這個概念類似馬桶的沖水器,你先把沖水器用塞子堵住,然后開始接水,等水滿了,你一次把塞子拔掉,沖水器中的水就一次沖出去了。在真正沖水之前,你就有機會把數(shù)據(jù)進行合并,排序,保證你的“電梯”可以從一頭走到另一頭,然后從另一頭回來。

我們前面講IO系統(tǒng)的時候就提過磁盤調(diào)度子系統(tǒng)的ftrace跟蹤,這里我們深入看看blktrace跟蹤到的事件的含義:

請求相關(guān)

Q - queued:bio請求進入調(diào)度
G - get request:分配request
I - inserted:request進入io調(diào)度器

調(diào)度相關(guān)

B - bounced:硬件無法訪問內(nèi)存,需要把內(nèi)存降低到硬件可訪問
M - back merge:請求和前一個從后面合并
F - front merge:請求和前一個從前面合并
X - split:請求分析為多個request(很可能是因為硬件不支持太大的請求)
A - remap:基于分區(qū)等,重新映射request的位置
R - requeue: Request重新回到調(diào)度隊列
S - sleep:調(diào)度器進入休眠P - plug:調(diào)度隊列插入設(shè)備(準備合并)
U - unplug:調(diào)度隊列離開設(shè)備(全部一次寫入設(shè)備中)
T - unplug due to timer超時,而不是數(shù)據(jù)足夠發(fā)起的unplug

發(fā)出相關(guān)

C - complete:完成一個request的調(diào)度(無論成功還是失?。?br> D - issued:發(fā)送到設(shè)備,這個是從下層硬件驅(qū)動發(fā)起的

我們通過對這些事件的跟蹤,對照硬件的特性大概就可以知道運行的模型是否正常了。

Block Layer 中的 IO Scheduler 三個調(diào)度算法:

  • noop,是 no operation,就是不調(diào)度的算法。有什么請求都直接寫下去。這通常用于兩種情形:你的磁盤是比如SSD那樣的內(nèi)存存儲設(shè)備,根本不需要調(diào)度,往下寫就對了。第二種情形是你的磁盤比較高級,自帶調(diào)度器,OS不需要自作聰明,有什么請求直接往下扔就好了。這兩種情況就應(yīng)該選noop算法。

  • deadline,是一個改良的電梯算法,基本上和電梯算法一樣,但加了一條,如果部分請求等太久了(deadline到了,默認讀請求500ms,寫請求5s),電梯就要立即給我掉頭,先處理這個請求。

  • cfg,CPU調(diào)度器,完全公平調(diào)度器。這個算法按任務(wù)分成多個隊列,按隊列的“完全公平”進行調(diào)度。利用這個算法,可以通過ionice設(shè)定每個任務(wù)不同的優(yōu)先級,提供給調(diào)度器進行分級調(diào)度。
    https://zhuanlan.zhihu.com/p/22604682

首先來看一下一般的IO調(diào)用。在傳統(tǒng)的文件IO操作中,我們都是調(diào)用操作系統(tǒng)提供的底層標準IO系統(tǒng)調(diào)用函數(shù) read()、write() ,此時調(diào)用此函數(shù)的進程(在JAVA中即java進程)由當(dāng)前的用戶態(tài)切換到內(nèi)核態(tài),然后OS的內(nèi)核代碼負責(zé)將相應(yīng)的文件數(shù)據(jù)讀取到內(nèi)核的IO緩沖區(qū),然后再把數(shù)據(jù)從內(nèi)核IO緩沖區(qū)拷貝到進程的私有地址空間中去,這樣便完成了一次IO操作。如下圖所示:

注意兩點:

  • OS的read函數(shù)會在內(nèi)核IO緩沖區(qū)中預(yù)讀取數(shù)據(jù),減少磁盤IO操作。
  • Java的BufferedReader或BufferedInputStream的緩沖區(qū)的作用是減少系統(tǒng)調(diào)用。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容