Linux塊設備I/O棧淺析

Linux存儲系統包括兩個部分:第一部分是給在用戶的角度提供讀/寫接口,數據以流爲表現形式;第二部分是站在存儲設備的角度提供讀/寫接口,數據以塊爲表現形式。文件系統位於兩者中間起到承上啓下的作用。

以塊爲表現形式,既塊存儲,簡單來說就是使用塊設備來爲系統提供存儲服務。本問重點在於塊設備的IO棧。



0x01 塊設備基本概念

塊設備將信息存儲在固定大小的塊中,每個塊都有自己的地址。對操作系統來說,塊設備是以字符設備的外觀展現的,雖然對這種字符設備可以按照字節爲單位進行訪問,但是實際上到塊設備上卻是以塊爲單位(最小512byte,既一個扇區)。這之間的轉換是由操作系統來完成的。

下面介紹塊設備的基本概念:

  1. 扇區:磁盤盤片上的扇形區域,邏輯化數據,方便管理磁盤空間,是硬件設備數據傳輸的基本單位,一般爲512byte。
  2. 塊:塊是VFS(虛擬文件系統)和文件系統數據傳輸的基本單位,必須是扇區的整數倍,格式化文件系統時,可以指定塊大小。

0x02 塊設備I/O棧

1 基本概念

本節介紹幾個塊設備I/O棧的基本概念。

  1. bio:bio是通用塊層I/O請求的數據結構,表示上層提交的I/O求情。一個bio包含多個page(既page cache 內核緩衝頁 在內存上),這些page對應磁盤上的一段連續的空間。由於文件在磁盤上並不連續存放,文件I/O提交到塊這杯之前,極有可能被拆分成多個bio結構。
  2. request:表示塊設備驅動層I/O請求,經由I/O調度層轉換後(既電梯算法合併)的I/O請求,將會被髮送到塊設備驅動層進行處理。
  3. request_queue: 維護塊設備驅動層I/O請求的隊列,所有的request都插入到該隊列,每個磁盤設備都只有一個queue(多個分區也只有一個)。

這三個結構的關係如下圖所示,一個request_queue中包含多個request,每個request可能包含多個bio,請求的合併就是根據各種算法(1.noop 2.deadline 3.CFQ)將多個bio加入到同一個request中。


2. 請求處理流程

在這裏插入圖片描述

先說一個Direct I/O和Buffer I/O的區別:

  1. Direct I/O繞過page cache,Buffer I/O是寫到page cache中表示寫請求完成,然後由文件系統的刷髒頁機制把數據刷到硬盤。因此,使用Buffer I/O,掉電時有可能page cache中的髒數據還未刷到磁盤上,導致數據丟失。
  2. Buffer I/O機制中,DMA方式可以將數據直接從磁盤讀到page cache中(與直接讀不同的是,不需要CPU參與),或者從page cache中將數據寫回到磁盤上,而不能直接在應用程序地址空間和磁盤之間進行數據傳輸,這樣的話,數據在傳輸過程中需要在應用程序地址空間和page cache之間進行多次數據拷貝操作,這些數據拷貝操作所帶來的CPU以及內存的開銷是非常大的(磁盤到page cache使用DMA CPU不參與,page cache到應用地址空間的數據複製需要CPU的參與)。而Direct I/O的優點就是通過減少操作系統內核緩衝區和應用程序地址空間的數據拷貝次數,降低了對文件讀取和寫入時所帶來的的CPU的使用和內存的佔用,但是Direct I/O的讀操作不能從page cache中獲取數據,而是直接從磁盤上讀取,帶來性能上的損失。爲了解決這個問題,Direct I/O會與異步I/O結合起來使用。
  3. Direct I/O一般用於需要自己管理緩存的應用,如數據庫系統。

下面說I/O的讀寫流程,簡單描述如下:

  1. 用戶調用系統調用write寫一個文件,會調到sys_write函數
  2. 經過VFS虛擬文件系統層,調用vfs_write, 如果是緩存寫方式,則寫入page cache,然後就返回,後續就是刷髒頁的流程;如果是Direct I/O的方式,就會走到do_blockdev_direct_IO的流程
  3. 如果操作的設備是邏輯設備如LVM(logical volume manager),MDRAID設備等,會進入到對應內核模塊的處理函數裏進行一些處理,否則就直接構造bio請求,調用submit_bio往具體的塊設備下發請求,submit_bio函數通過generic_make_request轉發bio,generic_make_request是一個循環,其通過每個塊設備下注冊的make_request_fn函數與塊設備進行交互
  4. 請求下發到底層的塊設備上(應該是屬於I/O調度層),調用塊設備請求處理函數__make_request進行處理,在這個函數中就會調用blk_queue_bio,這個函數就是合併bio到request中,也就是I/O調度器的具體實現:如果幾個bio要讀寫的區域是連續的,就合併到一個request;否則就創建一個新的request,把自己掛到這個request下。合併bio請求也是有限度的,如果合併後的請求超過閾值(在/sys/block/xxx/queue/max_sectors_kb裏設置),就不能再合併成一個request了,而會新分配一個request
  5. 接下來的I/O操作就與具體的物理設備有關了,交由相應的塊設備驅動程序進行處理,這裏以scsi設備爲例說明,queue隊列的處理函數request_fn對應的scsi驅動的就是scsi_request_fn函數,將請求構造成scsi指令下發到scsi設備進行處理,處理完成後就會依次調用各層的回調函數進行完成狀態的一些處理,最後返回給上層用戶

3. bcache

bcache是Linux內核的塊層緩存,它使用固態硬盤作爲硬盤驅動器的緩存,既解決了各臺硬盤容量太小的問題,有解決了硬盤驅動器運行速度太慢的問題。

bcache從3.10版本開始被集成進內核,支持三種緩存策略,分別是寫回(writeback)、寫透(writethrough)、writearound, 默認使用writethrough,緩存策略可被動態修改。、


0x03 參考

  1. 《Linux開源存儲全棧詳解》
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章