[原] KVM 虛擬化原理探究(6)— 塊設備IO虛擬化

KVM 虛擬化原理探究(6)— 塊設備IO虛擬化

標籤(空格分隔): KVM算法


塊設備IO虛擬化簡介

上一篇文章講到了網絡IO虛擬化,做爲另一個重要的虛擬化資源,塊設備IO的虛擬化也是一樣很是重要的。同網絡IO虛擬化相似,塊設備IO也有全虛擬化和virtio的虛擬化方式(virtio-blk)。現代塊設備的工做模式都是基於DMA的方式,因此全虛擬化的方式和網絡設備的方式接近,一樣的virtio-blk的虛擬化方式和virtio-net的設計方式也是同樣,只是在virtio backend端有差異。vim

傳統塊設備架構

塊設備IO協議棧

image_1aqo0ufta16pqlsm7nvuvc1ur71g.png-133.1kB

如上圖所示,咱們把塊設備IO的流程也看作一個TCP/IP協議棧的話,從最上層提及。緩存

Page cache層,這裏若是是非直接IO,寫操做若是在內存夠用的狀況下,都是寫到這一級後就返回。在IO流程裏面,屬於writeback模式。 須要持久化的時候有兩種選擇,一種是顯示的調用flush操做,這樣此文件(以文件爲單位)的cache就會同步刷到磁盤,另外一種是等待系統自動flush。安全

VFS,也就是咱們一般所說的虛擬文件系統層,這一層給咱們上層提供了統一的系統調用,咱們經常使用的create,open,read,write,close轉化爲系統調用後,都與VFS層交互。VFS不只爲上層系統調用提供了統一的接口,還組織了文件系統結構,定義了文件的數據結構,好比根據inode查找dentry並找到對應文件信息,並找到描述一個文件的數據結構struct file。文件實際上是一種對磁盤中存儲的一堆零散的數據的一種描述,在Linux上,一個文件由一個inode 表示。inode在系統管理員看來是每個文件的惟一標識,在系統裏面,inode是一個結構,存儲了關於這個文件的大部分信息。這個數據結構有幾個回調操做就是提供給不一樣的文件系統作適配的。下層的文件系統須要實現file_operation的幾個接口,作具體的數據的讀寫操做等。網絡

struct file_operations {
    //文件讀操做
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    //文件寫操做
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    int (*readdir) (struct file *, void *, filldir_t);
    //文件打開操做
    int (*open) (struct inode *, struct file *);
};

再往下就是針對不一樣的文件系統層,好比咱們的ext3,ext4等等。 咱們在VFS層所說的幾個文件系統須要實現的接口,都在這一層作真正的實現。這一層的文件系統並不直接操做文件,而是經過與下層的通用塊設備層作交互,爲何要抽象一層通用塊設備呢?咱們的文件系統適用於不一樣的設備類型,好比多是一個SSD盤又或者是一個USB設備,不一樣的設備的驅動不同,文件系統沒有必要爲每一種不一樣的設備作適配,只須要兼容通用快設備層的接口就能夠。數據結構

位於文件系統層下面的是通用快設備層,這一層在程序設計裏面是屬於接口層,用於屏蔽底層不一樣的快設備作的抽象,爲上層的文件系統提供統一的接口。架構

通用快設備下層就是IO調度層。用以下命令能夠看到系統的IO調度算法.函數

➜  ~ cat /sys/block/sda/queue/scheduler
noop deadline [cfq]
  1. noop,能夠當作是FIFO(先進先出隊列),對IO作一些簡單的合併,好比對同一個文件的操做作合併,這種算法適合好比SSD磁盤不須要尋道的塊設備。oop

  2. cfq,徹底公平隊列。此算法的設計是從進程級別來保證的,就是說公平的對象是每一個進程。系統爲此算法分配了N個隊列用來保存來自不一樣進程的請求,當進程有IO請求的時候,會散列到不一樣的隊列,散列算法是一致的,同一個進程的請求老是被散列到同一個隊列。而後系統根據時間片輪訓這N個隊列的IO請求來完成實際磁盤讀寫。

  3. deadline,在Linux的電梯調度算法的基礎上,增長了兩個隊列,用來處理即將超時或者超時的IO請求,這兩個隊列的優先級比其餘隊列的優先級較高,因此避免了IO飢餓狀況的產生。

塊設備驅動層就是針對不一樣的塊設備的真實驅動層了,塊設備驅動層完成塊設備的內存映射並處理塊設備的中斷,完成塊設備的讀寫。

塊設備就是真實的存儲設備,包括SAS,SATA,SSD等等。塊設備也可能有cache,通常稱爲Disk cache,對於驅動層來講,cache的存在是很重要的,好比writeback模式下,驅動層只須要寫入到Disk cache層就能夠返回,塊設備層保證數據的持久化以及一致性。
一般帶有Disk cache的塊設備都有電池管理,當掉電的時候保證cache的內容可以保持一段時間,下次啓動的時候將cache的內容寫入到磁盤中。

塊設備IO流程

應用層的讀寫操做,都是經過系統調用read,write完成,由Linux VFS提供的系統調用接口完成,屏蔽了下層塊設備的複雜操做。write操做有直接IO和非直接IO之分(緩衝IO),非直接IO的寫操做直接寫入到page cache後就返回,後續的數據依賴系統的flush操做,若是在flush操做未完成的時候發生了系統掉電,那可能會丟失一部分數據。直接IO(Direct IO),繞過了page cache,數據必須達到磁盤後才返回IO操做完成。

對於I/O的讀寫流程,邏輯比較複雜,這裏以寫流程簡單描述以下:
image_1aqo0lnfn6m8ei6lef1qg019p713.png-46.9kB

  1. 用戶調用系統調用write寫一個文件,會調到sys_write函數;
  2. 通過VFS虛擬文件系統層,調用vfs_write,若是是緩存寫方式,則寫入page cache,而後就返回,後續就是刷髒頁的流程;若是是Direct I/O的方式,就會走到塊設備直接IO(do_blockdev_direct_IO)的流程;
  3. 構造bio請求,調用submit_bio往具體的塊設備下發請求,submit_bio函數經過generic_make_request轉發bio,generic_make_request是一個循環,其經過每一個塊設備下注冊的q->make_request_fn函數與塊設備進行交互;
  4. 請求下發到底層的塊設備上,調用塊設備請求處理函數__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操做就與具體的物理設備有關了,塊設備驅動的讀寫也是經過DMA方式進行。
    image_1aqnul1bnu1b1o3l14m81es11r72m.png-19.8kB

如上圖所示,在初始化IO設備的時候,會爲IO設備分配一部分物理內存,這個物理內存能夠由CPU的MMU和鏈接IO總線的IOMMU管理,做爲共享內存存在。以一個讀取操做爲例子,當CPU須要讀取塊設備的某個內容的時候,CPU會經過中斷告知設備內存地址以及大小和須要讀取的塊設備地址,而後CPU返回,塊設備完成實際的讀取數據後,將數據寫入到共享的內存,並以中斷方式通知CPU IO流程完成,並設置內存地址,接着CPU直接從內存中讀取數據。
寫請求相似,都是經過共享內存的方式,這樣能夠解放CPU,不須要CPU同步等待IO的完成而且不須要CPU作過多的運算操做。
由於塊設備IO的虛擬化須要通過兩次IO協議棧,一次Guest,一次HV。因此須要把塊設備IO協議棧說的很具體一點。

至此,Linux塊設備的IO層就基本介紹完整了,以上內容也只是作一個簡單的介紹,這部分的內容能夠很深刻的去了解,在此限於篇幅限制,就不作過多介紹了。

塊設備IO虛擬化

塊設備的全虛擬化方式和網絡IO的DMA設備虛擬化方式相似,這裏就不過多介紹了,主要介紹一下virtio-blk。

image_1aqo1nvbo1o615hn2kuptlbso1t.png-52.7kB

如上圖所示,塊設備IO的虛擬化流程和網絡IO的流程基本一致,差異在於virtio-backend一段,virtio-net是寫入到tap設備,virtio-blk是寫入到鏡像文件中。
塊設備IO的流程須要通過兩次IO協議棧,一次位於Guest,一次位於HV。當咱們指定virtio的cache模式的時候,實際上指定的是virtio-backend(下面簡稱v-backend)寫入HV塊設備的方式。
在虛擬化層次來看,Guest對於這幾種Cache模式是沒有感知的,也就是不管Cache模式是怎樣,Guest都不會有所謂的繞過Guest的Page cache等操做,Virtio-front模擬的是驅動層的操做,不會涉及到更上層的IO協議棧。

image_1aqo7picj8ks1qng1lt51errfvim.png-62.7kB

如上圖所示,藍色表示 writethrough,黃色表示 none,紅色表示 writeback。其中虛線表示寫到哪個層次後write調用返回。

  • cache=writethrough (藍色線)
    表示v-backend打開鏡像文件並寫入時候採用非直接IO+flush操做,也就是說每次寫入到Page cache並flush一次,直到數據被真實寫入到磁盤後write調用返回,這樣必然會致使數據寫入變慢,可是好處就是安全性較高。

  • cache=none (黃色線)
    cache爲none模式表示了v-backend寫入文件時候使用到了DIRECT_IO,將會繞過HV的Page cache,直接寫入磁盤,若是磁盤有Disk cache的話,寫入Disk cache就返回,此模式的好處在於保證性能的前提下,也能保證數據的安全性,在使用了Disk cache電池的狀況下。可是對於讀操做,由於沒有寫入HV Page cache,因此會有必定性能影響。

  • cache=writeback (紅色線)
    此模式表示v-backend使用了非直接IO,寫入到HV的Page後就返回,有可能會致使數據丟失。

總結

塊設備IO的虛擬化方式也是統一的virtio-x模式,可是virtio-blk須要通過兩次IO協議棧,帶來了沒必要要的開銷。前面的鋪墊都是爲了介紹三種重要的cache模式。

相關文章
相關標籤/搜索