Linux Storage Stack Diagram

工做學習中,沒有比圖表更好的東西了(雖然不少人在嘲笑PPT),尤爲是描述精準的圖表。當你想畫圖說明一個結構或一個流程時,必須對其已經充分理解。而在講解一張圖時,也必須對其有基本的理解。這真的不簡單,反正對我來講是這樣。關於Linux Storage架構,就有一張描述很精準的圖,「Linux Storage Stack Diagram」。這張圖總結的實在是太好了,Storage涉及的模塊都有描述,讓學習者能清晰的瞭解複雜的系統。本文試圖對該圖的各部分作個簡介,但不會涉及具體的實現。前端

https://www.thomas-krenn.com/...linux

圖中使用顏色來區分不一樣的組成部分。算法

  • 天藍色:硬件存儲設備。
  • 橙色:傳輸協議。
  • 藍色:Linux系統中的設備文件。
  • 黃色:I/O調度策略。
  • 綠色:Linux系統的文件系統。
  • 藍綠色:Linux Storage操做的基本數據結構BIO。

文件系統

VFS

VFS是Linux內核提供的一個虛擬文件系統層。VFS提供給用戶層一些標準的系統調用來操做文件系統,如open()、read()、write()等,讓用戶態應用無需關心底層的文件系統和存儲介質。同時VFS還要對底層文件系統進行約束,提供統一的抽象接口和操做方式。後端

底層文件系統

Linux支持的文件系統衆多,大體能夠分爲如下幾類。緩存

  • 磁盤文件系統:基於物理存儲設備的文件系統,用來管理設備的存儲空間,如ext二、ext四、xfs等。
  • 網絡文件系統:用於訪問網絡中其餘設備上的文件,如NFS、smbfs等。網絡文件系統的目標是網絡設備,因此它不會調用系統的Block層。
  • 堆棧式文件系統:疊加在其餘文件系統之上的一種文件系統,自己不存儲數據,而是對下層文件的擴展,如eCryptfs,Wrapfs等。
  • 僞文件系統:由於並無論理真正的存儲空間,因此被稱爲僞文件系統。它組織了一些虛擬的目錄和文件,經過這些文件能夠訪問系統或硬件的數據。它不是用來存儲數據的,而是把數據包裝層文件用來訪問,因此不能把僞文件系統當作存儲空間來操做。如proc、sysfs等。
  • 特殊文件系統:特殊文件系統也是一種僞文件系統,它使用起來更像是一個磁盤文件系統,但讀寫是內存而不是磁盤設備。如tmpfs、ramfs等。
  • 用戶文件系統:也叫作FUSE。它提供一種方式可讓開發者在用戶空間實現文件系統,而不須要修改內核。這種方式更加靈活,但效率會更低。FUSE直接面向的是用戶文件系統,也不會調用Block層。

Block Layer

Block Layer是Linux Storage系統中的中間層,鏈接着文件系統和塊設備。它將上層文件系統的讀寫請求抽象爲BIOs,經過調度策略將BIOs傳輸給設備。Block Layer包含圖中的藍綠色、黃色和中間BIOs傳輸過程。網絡

Page cache

Linux系統在打開文件時能夠經過O_DIRECT標識來卻別是否使用Page cache。當帶有O_DIRECT時,I/O讀寫會繞過cache,直接訪問塊設備。不然,讀寫須要經過Page cache進行,Page cache的主要行爲以下。數據結構

  • 讀數據時,若是訪問的頁在Page cache中(命中),則直接返回頁。
  • 讀數據時,若是訪問的頁不在Page cache中(缺失),則產生缺頁異常。系統會建立一個緩存頁,將訪問的地址緩存到這個頁中。上層會再次讀取,發生cache命中。
  • 寫數據時,若是cache命中,則將數據寫到緩存頁中。
  • 寫數據時,若是cache缺失,則產生缺頁異常,系統建立緩存頁。上層再次寫入,發生cache命中。
  • 當Page cache中的一個緩存頁被修改時,會標記爲dirty。上層調用sync或pdflush進程會將髒頁寫回到磁盤中。

BIO

BIO表明對Block設備的讀寫請求,在內核中使用一個結構體來描述。架構

struct bvec_iter {
    sector_t        bi_sector; // 設備地址,以扇區(512字節)爲單位
    unsigned int        bi_size; // 傳輸數據的大小,byte
    unsigned int        bi_idx;    // 當前在bvl_vec中的索引
    unsigned int        bi_bvec_done; // 當前bvec中已經完成的數據大小,byte
};

struct bio {
    struct bio        *bi_next;    // request隊列
    struct block_device    *bi_bdev; // 指向block設備
    int            bi_error;
    unsigned int        bi_opf;        // request標籤
    unsigned short        bi_flags;    // 狀態,命令
    unsigned short        bi_ioprio;
    struct bvec_iter    bi_iter;
    unsigned int        bi_phys_segments; // 物理地址合併後,BIO中段的數量

    /*
     * To keep track of the max segment size, we account for the
     * sizes of the first and last mergeable segments in this bio.
     */
    unsigned int        bi_seg_front_size; // 第一個可合併段的大小
    unsigned int        bi_seg_back_size; // 最後一個可合併段的大小

    atomic_t        __bi_remaining;
    bio_end_io_t        *bi_end_io; // BIO結束時的回調函數,通常用於通知調用者該BIO的完成狀況 
    ......
    unsigned short        bi_vcnt; // bio_vec的計數
    unsigned short        bi_max_vecs; // bvl_vecs的最大數量
    atomic_t        __bi_cnt; // 使用計數
    struct bio_vec        *bi_io_vec; // vec list的指針
    struct bio_set        *bi_pool;
    ......
};

一個BIO構建完成後,就能夠經過generic_make_request()來建立傳輸Request,將Request加入到請求隊列中。請求隊列在內核中有結構體request_queue來描述,它包含一個雙向請求鏈表以及相關控制信息。請求鏈表中每一項都是一個Request,Request由BIOs組成,BIO中又可能包含不一樣的Segment。由於一個BIO只能連續的磁盤塊,但一個Request可能不連續的磁盤塊,因此一個Request可能包含一個或多個BIOs。儘管BIO中的磁盤塊是連續的,但它們在內存中多是不連續的,因此BIO中可能包含幾個Segments。app

Scheduler

讀寫數據組織成請求隊列後,就是訪問磁盤的過程,這個過程由IO調度完成。BIOs訪問的指定的磁盤扇區,首先要進行尋址的操做。尋址就是定位磁盤磁頭到特定塊上的某個位置,這個過程相對來講很慢。爲了優化尋址操做,內核既不會簡單地按請求接收次序,也不會當即將其提交給磁盤,而是在提交前,先執行名爲合併與排序的預操做,這種預操做能夠極大地提升系統的總體性能。這就是IO調度須要完成的工做。socket

當前內核中,支持兩種模式的IO調度器:single-queue和multi-queue。single-queue在圖中標識爲「I/O Scheduler」,multi-queue標識爲blkmq。兩者應該都是Scheduler,只是請求的組織方式不一樣。

single-queue經過合併和排序來減小磁盤尋址時間。合併指將多個連續請求合成一個更大的IO請求,以便充分發揮硬件性能。排序使用電梯調度,將整個請求隊列將按扇區增加方向有序排列。排列的目的不只是爲了縮短單獨一次請求的尋址時間,更重要的優化在於,經過保持磁盤頭以直線方向移動,縮短了全部請求的磁盤尋址的時間。目前single-queue使用的調度策略包括:noop、deadline、cfq等。

  • Noop:IO調度器最簡單的算法,將IO請求放入隊列中並順序的執行這些IO請求,對於連續的IO請求也會作相應的合併。
  • Deadline:保證IO請求在必定時間內可以被服務,避免某個請求飢餓。
  • CFQ:即絕對公平算法,試圖爲競爭塊設備使用權的全部進程分配一個請求隊列和一個時間片,在調度器分配給進程的時間片內,進程能夠將其讀寫請求發送給底層塊設備,當進程的時間片消耗完,進程的請求隊列將被掛起,等待調度。

早先的內核只有single-queue,當時存儲設備主要時HDD,HDD的隨機尋址性能不好,single-queue就能夠知足傳輸需求。當SSD發展起來後,它的隨機尋址性能很好,傳輸的瓶頸就轉移到請求隊列上。結合多核CPU,multi-queue被設計出來。multi-queue爲每一個CPU core或socket配置一個Software queue,這也解決了single-queue中多核鎖競爭的問題。若是存儲設備支持並行多個Hardware dispatch queues,傳輸性能又會大幅度提高。目前multi-queue支持的調度策略包括:mq-deadline、bfq、kyber等。

Block設備

設備文件

設備文件是Linux系統訪問硬件設備的接口,驅動程序將硬件設備抽象爲設備文件,以便應用程序訪問。設備驅動加載時在/dev/下建立設備文件描述符,若是是Block設備,同時會建立一個軟連接到/dev/block/下,並根據設備號來命名。圖中將Block設備分爲如下幾類。

  • 邏輯設備:圖中的「Devices on top of "normal" block devices",使用Device Mapper將物理塊設備進行映射。經過這種映射機制,能夠根據須要實現對存儲資源的管理。包括LVM、DM、bcache等。
  • SCSI設備:使用SCSI標準的設備文件,包括sda(硬盤)、sr(光驅)等。
  • 其餘塊設備:每一種塊設備都有本身的傳輸協議。一類表明真正的硬件設備,如mmc、nvme等。另外一類表示虛擬的塊設備,如loop、zram等。

傳輸協議

圖中橙色部分表示了Block設備所依賴的技術實現,多是硬件規範的軟件實現,也多是一種軟件架構。圖中把SCSI和LIO單獨圈出來,由於這兩部分相對比較複雜。SCSI包含的硬件規範不少,最經常使用的是經過libata來訪問HDD和SSD。

LIO(Linux-IO)是基於SCSI engine,實現了SCSI體系模型(SAM)中描述的SCSI Target。LIO在linux 2.6.38後引入內核,其支持的SAN技術包括Fibre Channel、FCoE、iSCSI、iSER 、SRP、USB等,同時還能爲本機生成模擬的SCSI設備,以及爲虛擬機提供基於virtio的SCSI設備。LIO使用戶可以使用相對廉價的Linux系統實現SCSI、SAN的各類功能,而不用購買昂貴的專業設備。能夠看到LIO的前端是Fabric模塊(Fibre Channel、FCoE、iSCSI等),用來訪問模擬的SCSI設備。Fabric模塊就是實現SCSI命令的傳輸協議,例如iSCSI技術就是把SCSI命令放在TCP/IP中傳輸,vhost技術就是把SCSI命令放在virtio隊列中傳輸。LIO的後端實現了訪問磁盤數據的方法。FILEIO經過Linux VFS來訪問數據,IBLOCK訪問Linux Block設備,PSCSI 直接訪問SCSI設備,Memory Copy RAMDISK用來放訪問模擬SCSI的ramdisk。

硬件設備

圖中天藍色部分,就是實際的硬件存儲設備。其中virtio_pci、para-virtualized SCSI、VMware's para-virtualized scsi是虛擬化的硬件設備。

相關文章
相關標籤/搜索