您當前的位置:首頁 > 繪畫

簡述 Linux IO 原理及零複製(上)— 磁碟 IO

作者:由 CPP加油站 發表于 繪畫時間:2021-12-21

1 簡述

什麼是零複製?零複製其實是泛指減少 CPU 複製資料的技術。要討論“減少”,必須得弄清楚原本為什麼需要。

所以:想理解好零複製,重點還在於理解為什麼需要複製,以及不同零複製技術的對比。想理解好 I/O 原理,必須先弄清楚資料結構。

掌握了下面這張圖,瞭解各每個元件的作用,自然就理解了 I/O 及零複製。

本文重點在於總結 Linux I/O 相關知識之間的聯絡,幫助讀者系統的瞭解相關原理,因此不會很深入地討論某一個技術點。另外,本文只包含磁碟 I/O 部分及部分零複製內容,後續還會分享網路I/O,敬請期待《簡述 Linux IO 原理及零複製(中)——網路 I/O》。

**綠色的圖形表示資料儲存的位置,綠色的箭頭則表示資料的複製。

簡述 Linux IO 原理及零複製(上)— 磁碟 IO

圖-1

2 劃分

從左到右,Linux IO 包含兩部分:磁碟 IO 和網路 IO,這個大家都能理解。

從上到下,儲存又被劃分為三部分:使用者空間、核心空間以及物理裝置。

2。1 從上到下,為什麼劃分為三層?

我們既然講資料複製,肯定是按是否存在資料複製來劃分的。記憶體和外儲存是不同的硬體介質,他們之間相互訪問自然是靠資料複製的,所以要分開。

另外,Linux 作業系統為了安全考慮,其核心管理了幾乎所有的硬體裝置,不允許使用者程序直接訪問。因此,邏輯上計算機被分為使用者空間和核心空間(外設及其驅動是被劃分在核心空間的)。

執行在使用者空間的程序就是使用者態,執行在核心空間的程序就是核心態。使用者態的程式,訪問不了核心空間的資料,所以就需要由核心態的程序把資料複製到使用者態。

磁碟I/O

1 快取 I/O (Buffered I/O)

理解了上面講的分層邏輯,我們拿 read 舉例。

資料從硬碟複製到核心空間,再從核心空間複製到使用者空間,這種 I/O 方式就是標準 I/O。

當執行緒多次訪問同一份磁碟上的資料時,Linux 並不會傻傻地多次訪問磁碟。因為磁碟相對記憶體來時實在是太慢了,為了減少讀盤的次數,被載入在核心空間的那份資料會被重複使用(頁快取/Page cache)。

同理,執行緒的寫入操作也會先寫入頁快取中,在根據 Linux 的延遲寫機制,寫入到磁碟中。因此,標準 I/O 又被稱作快取 I/O 大多數檔案系統的預設 I/O 操作都是快取 I/O。

1。1 Page cache 和 Buffer cache

講到了 Buffered I/O,就得講一下 Linux 是怎麼管理核心空間中那份資料快取的。早期的 Linux,把快取分為了兩種:

Page cache

Buffer cache

Buffer cache

也叫塊緩衝,是對物理磁碟上的一個磁碟塊進行的緩衝。其大小為通常為1k,磁碟塊也是磁碟的組織單位。設立 Buffer cache 的目的是為在程式多次訪問同一磁碟塊時,減少訪問時間。

Page cache

也叫頁緩衝或檔案緩衝。是由好幾個磁碟塊構成,大小通常為4k,在64位系統上為8k。構成的幾個磁碟塊在物理磁碟上不一定連續,檔案的組織單位為一頁,也就是一個Page cache大小。

檔案讀取是由外存上不連續的幾個磁碟塊,到 Buffer cache,然後組成 Page cache,然後供給應用程式。

從圖中可以看到 Page cache 是建立在檔案系統(Ext4)之上的,因此其快取的是邏輯資料 。Buffer cache 是建立在塊層之上的,因此其快取的是物理輯資料。

1。2 消失的 Buffer cache

Linux 大約在2。4。10之後的 disk cache 只有 Page cache。而 Buffer cache 只是 Page cache 中的 buffer_head 描述符。

換句話說,Page cache 和 Buffer cache 已經合併了。

(所以圖中 Buffer cache 是灰色的,為了更容易理解 IO 原理,黃色和灰色部分都可以不考慮了)

1。3 快取I/O的一致性和安全性

如果出現程序死,核心死,掉電這樣事件發生。資料會丟失嗎?

程序死:如果資料還處在 application cache 或 CLib cache 時候,資料會丟失。

核心死:即使進入了 page cache(完成了write),如果沒有進行 sync 操作,資料還是會丟失。

掉電:進行了 sync,資料就一定寫入了磁碟了嗎?答案是:不一定。

注意到圖-1中,磁碟旁邊的綠色圖形了嗎?它表示的是磁碟上的快取。寫資料達到一個程度時才真正寫入磁碟。

1。4 補充知識“磁碟的快取”

磁碟快取在磁碟上就表現為一塊 RAM 晶片。磁碟上必須有快取,用來接收指令和資料,還被用來進行預讀。磁碟快取分為

讀快取

寫快取

讀快取

是指,作業系統為已讀取的檔案資料,在記憶體較空閒的情況下留在記憶體空間中。當下次軟體或使用者再次讀取同一檔案時就不必重新從磁碟上讀取,從而提高速度。

寫快取

實際上就是將要寫入磁碟的資料先保存於系統為寫快取分配的記憶體空間中。當儲存到記憶體池中的資料達到一個程度時,便將資料儲存到硬碟中。這樣可以減少實際的磁碟操作,有效地保護磁碟免於重複的讀寫操作而導致的損壞,也能減少寫入所需的時間。

所謂磁碟快取的禁用是指的 Write Through 模式。即:磁碟收到寫入指令和資料後,必須先將其寫入碟片,然後才向控制器返回成功訊號(實際還是先寫入快取,再寫入碟片的),這樣就相當於“禁用”了快取。

對一致性要求高的應用,比如 DB/磁碟陣列,都會禁用磁碟寫快取的。

相關影片推薦

LinuxC++零複製的實現 使用者態協議棧 ntytcp

面試中正經“八股文”網路原理tcp/udp,網路程式設計epoll/reactor

6種epoll的設計,讓你吊打面試官,而且他不能還嘴

epoll原理剖析以及三握四揮的處理

LinuxC++後臺伺服器開發架構師免費學習地址

【文章福利】:小編整理了一些個人覺得比較好的學習書籍、影片資料共享在群檔案裡面,有需要的可以自行新增哦!~點選832218493加入(需要自取)

簡述 Linux IO 原理及零複製(上)— 磁碟 IO

2 DMA

DMA 是我們最常見的資料傳輸方式,DMA 的技術細節並不是本文介紹的重點。下文透過對比不同資料傳輸方式的差別,幫助我們理解 DMA 對於提升效能的重要意義。Linux 提供了4種磁碟與主存之間的資料傳輸機制。

直接控制(程式 I/O )(Programmed I/O)

基於迴圈對 I/O 埠進行不斷檢測。CPU 和 I/O 裝置只能序列工作,導致CPU的利用率相當低。

中斷驅動方式(Interrupt)

當資料到達時,磁碟主動向 CPU 發起中斷請求,由 CPU 自身負責資料的傳輸過程。由於資料中的每個字在儲存器與 I/O 控制器之間的傳輸都必須經過 CPU,這就導致了中斷驅動方式仍然會消耗較多的 CPU 時間。而且整個過程中,中斷次數很多,導致太多次的上下文切換,所以效能很差。

DMA(直接記憶體訪問)方式

DMA 是一種與 CPU 共享記憶體匯流排的裝置。它可以代替 CPU,把資料從記憶體到裝置之間進行複製。僅在傳送一個或多個數據塊的開始和結束時,才需 CPU 干預(傳送 DMA 中斷),整塊資料的傳送是在 DMA控制器的控制下完成的。

通道控制方式(不常見)

通道相當於一個功能簡單的處理機。包含通道指令(讀,寫,控制,轉移),並可執行用這些指令編寫的通道程式。I/O 通道方式是 DMA 方式的加強,它可以進一步減少 CPU 的干預。即把對一個數據塊的讀(或寫)為單位的干預,減少為對一組資料塊的讀(或寫)及有關的控制和管理為單位的干預。同時,又可以實現 CPU、通道和 I/O 裝置三者的並行操作,從而更有效地提高整個系統的資源利用率。

2。1 不同傳輸機制的流程圖

簡述 Linux IO 原理及零複製(上)— 磁碟 IO

圖-2

2。2 不同傳輸機制的時序圖

簡述 Linux IO 原理及零複製(上)— 磁碟 IO

圖-3

2。3 I/O 通道與 DMA 方式的區別

DMA 方式需要 CPU 來控制傳輸的資料塊大小、傳輸的記憶體位置,而通道方式中這些資訊是由通道控制的。

每個 DMA 控制器對應一臺裝置與記憶體傳遞資料,而一個通道可以控制多臺裝置與記憶體的資料交換。

2。4 總結

PIO,中斷 IO 都屬於 CPU 複製。通道控制,是 DMA 的增強,嚴格來講屬於 DMA 技術。

I/O 通道並不常見,而程式 I/O,中斷 I/O 的效率又太低了。所以,我們常見的磁碟 I/O 和網路 I/O 都是 DMA 方式,後文中也不再提及其他傳輸機制。

2。5 快取 I/O 的缺點

在快取 I/O 機制中,DMA 方式可以將資料直接從磁碟讀到頁快取中,或者將資料從頁快取直接寫回到磁碟上,而不能直接在應用程式地址空間和磁碟之間進行資料傳輸。這樣的話,資料在傳輸過程中需要在應用程式地址空間和頁快取之間進行多次資料複製操作,這些資料複製操作所帶來的 CPU 以及記憶體開銷是非常大的。

對於某些特殊的應用程式來說,避開作業系統核心緩衝區,而直接在應用程式地址空間和磁碟之間傳輸資料,會比使用作業系統核心緩衝區獲取更好的效能,因此引入“Direct I/O”。

3 直接 I/O(Direct I/O)

從2。6。0核心開始支援直接 I/O。

凡是透過直接 I/O 方式進行資料傳輸,資料均直接在使用者地址空間的緩衝區和磁碟之間直接進行傳輸,完全不需要頁快取的支援。

程序在開啟檔案的時候設定對檔案的訪問模式為 O_DIRECT ,這樣就等於告訴作業系統程序在接下來使用 read() 或者 write() 系統呼叫去讀寫檔案的時候使用的是直接 I/O 方式,所傳輸的資料均不經過作業系統核心快取空間。

使用直接 I/O 讀寫資料必須要注意緩衝區對齊( buffer alignment )。

從第一張圖中可以看到,Direct I/O 跨過了檔案系統,由塊裝置執行直接 I/O 提供的支援,因此 O_DIRECT 要求的對齊基本單位是底層塊裝置的邏輯塊大小(Logical Block Size 也叫 Sector Size)。

簡述 Linux IO 原理及零複製(上)— 磁碟 IO

3。1 直接 I/O 優點

最大的優點就是減少作業系統緩衝區和使用者地址空間的複製次數。降低了 CPU 的開銷,和記憶體頻寬。對於某些應用程式來說簡直是福音,將會大大提高效能。

3。2 直接 I/O 缺點

直接 I/O 並不總能讓人如意。直接 I/O 的開銷也很大,應用程式沒有控制好讀寫,將會導致磁碟讀寫的效率低下。磁碟的讀寫是透過磁頭的切換到不同的磁軌上讀取和寫入資料,如果需要寫入資料在磁碟位置相隔比較遠,就會導致尋道的時間大大增加,寫入讀取的效率大大降低。

3。3 直接 I/O 的補充

有的文章把直接 I/O 解釋成,資料直接跨過核心進行傳輸,容易造成誤解。使用者空間的程序是不允許訪問核心空間的,因此 Direct I/O 本質是 DMA 裝置把資料從使用者空間複製到裝置,或是從裝置複製到使用者空間。

不過事事總有例外,mmap 就是個特別的存在。

4 mmap

(文件化的定義)mmap 將一個檔案或者其它物件對映進記憶體。普通檔案被對映到程序地址空間後,程序可以像訪問普通記憶體一樣對檔案進行訪問,不必再呼叫read(),write()等操作。

簡述 Linux IO 原理及零複製(上)— 磁碟 IO

offset 是檔案中對映的起始位置,length 是對映的長度。

簡述 Linux IO 原理及零複製(上)— 磁碟 IO

圖-4

mmap 在使用者空間對映呼叫系統中作用很大。

4。1 mmap記憶體對映原理

簡述 Linux IO 原理及零複製(上)— 磁碟 IO

圖-5

mmap記憶體對映過程:

程序在虛擬地址空間中為對映建立虛擬對映區域。

核心把檔案物理地址和程序虛擬地址進行對映。

程序發起對這片對映空間的訪問,引發缺頁異常,實現檔案內容到物理記憶體(主存)的複製。

換句話說,在呼叫 mmap 後,只是在程序的虛擬空間中分配了一段空間,真實的物理地址還不會分配的。當程序第一次訪問這段空間(當作記憶體一樣),CPU 陷入 OS 核心執行異常處理。然後異常處理會在這個時間分配物理記憶體,並用檔案的內容填充這片記憶體,然後才返回程序的上下文,這時程序才會感知到這片記憶體裡有資料。

4。2 mmap本質

忘掉文件化的定義。

mmap 本質是記憶體共享機制,它把 page cache 地址空間對映到使用者空間,換句話說,mmap 是一種特殊的Buffered I/O。

因為底層有 CPU 的 MMU 支援,自然會轉換到物理區域,對於程序而言是無感知。所以,磁碟資料載入到 page cache 後,使用者程序可以透過指標操作直接讀寫 page cache,不再需要系統呼叫和記憶體複製。

因此,offset 必須是按 page size 對齊的(不對齊的話就會對映失敗)。

mmap 對映區域大小必須是物理頁大小(page size)的整倍數(32位系統中通常是4k)。length 對齊是靠核心來保證的,比如檔案長度是10KB,你映射了5KB,那麼核心會將其擴充到8KB。

4。3 mmap效能

簡述 Linux IO 原理及零複製(上)— 磁碟 IO

圖-6

瞭解 mmap 記憶體對映原理,有助於瞭解其效能。

mmap 減少了資料在核心空間與使用者空間的複製,從這個角度講,提高了檔案讀取效率。但是,mmap 在資料載入到 page cache 的過程中,會觸發大量的 page fault 和建立頁表對映的操作,開銷並不小。

另一方面,隨著硬體效能的發展,記憶體複製消耗的時間已經大大降低了。所以很多情況下,mmap 的效能反倒是比不過 read 和 write 的。

4。4 mmap的補充

有的文章把 mmap 形容成“跨過了頁快取”,因此減少了資料的複製次數。這種表述方式是錯誤的。

mmap 可分為共享對映和私有對映兩種。

共享對映

,修改對所有程序可見。也就是說,如果程序 A 修改了其中某個 page 上的資料,程序 B 之後讀取這個 page 得到的就是修改後的內容。

私有對映

,程序 A 的修改對程序 B 是不可見的,都是同一份資料,這是如何做到的呢?這裡利用的是 Copy On Write(COW) 機制。

當程序 A 試圖修改某個 page 上的資料時,核心會將這個 page 的內容複製一份。之後 A 的寫操作實際是在這個複製的 page 上進行的(程序 A 中對應這個page的頁表項也需要被修改,以指向新複製的 page),這樣程序 B 看到的這個 page 還是原來未經改動的。這種修改只會存在於記憶體中,不會同步到外部的磁碟檔案上(事實上也沒法同步,因為不同程序所做的修改是不同的)。

Page fault(頁缺失,又名缺頁中斷,缺頁異常),屬於硬體中斷,是由中央處理器的記憶體管理單元(MMU)所發出的中斷。

5 磁碟I/O總結

結婚後,你把工資卡上交給了老婆。你就是使用者態,只能管理自己的錢包。你老婆就是核心態,她可以訪問銀行,管理自己的錢包,也包括你的錢包。

快取 I/O

你需要錢的時候跟老婆申請。你老婆會從自己的錢包裡把錢放到你的錢包。如果她的錢包裡也沒錢了,她自己會去銀行取錢。

直接 I/O

你最近用錢比較頻繁,需要錢的時候同樣需要跟老婆申請。你老婆去銀行取了錢,直接放到了你的錢包裡。

mmap

你老婆允許你從她的錢包裡拿錢。你老婆錢包裡沒錢的時候,她自己會去銀行取錢。

CPU 複製/ DMA 複製

你丈母孃都是自己去銀行取錢的,這就是 CPU 複製。你老婆要忙著追劇,她懶得去取錢,讓銀行把錢送你家來,這就是 DMA 複製。實際上你老婆一直比較忙,她從來沒去過銀行,一直是銀行把錢送你家來的。

原創地址:簡述 Linux I/O 原理及零複製(上)— 磁碟 I/O

標簽: 磁碟  快取  cache  複製  核心