高性能磁盤 I/O 開發學習筆記 -- 硬件原理篇

曾經作嵌入式開發的我,如今作服務器開發,不少思路要轉變。今天學習了服務器高性能IO設計,同時本身也還發散開去學習了其餘的一些參考資料,順便結合本身已有的一些知識,作爲本身的學習筆記,總結和記錄一下吧~~html

本文首先從硬件原理的角度,闡述提升硬盤 I/O 效率的途徑。
本文包括一些小知識,與高性能服務器開發沒有直接關係,不感興趣的話能夠跳過。segmentfault

本文地址:http://www.javashuo.com/article/p-aadigvbs-hr.html緩存

「硬盤」 是什麼

這裏我所說的 「硬盤」,也就是所謂的 「hard disk」,常常簡稱爲 「disk」 或者 「HDD」,同時還有另一個更加高大上的名字 「非易失性存儲」。安全

請各位回憶一下計算機組成原理裏關於存儲的部分,從 CPU 開始,存儲層次以下:服務器

  • 寄存器
  • 緩存(cache),從高到低又能夠分一級、二級、三級緩存,數字越高,距離 CPU 越遠、容量越大、速度越慢
  • 主存,也就是內存,就是咱們常見說 「內存條」
  • 硬盤,包括全部的非易失性存儲,就是本文說明的內容。
  • 離線存儲,包括那些 CD 啊之類的

非易失性存儲就是說該存儲介質上的數據,只要寫入了,那麼就算設備掉電了,也可以保持,而不會被清空。
廣義而言,非易失性存儲包含很是多的種類,包括磁盤、閃存、EEPROM 等等。而對於服務器而言,只涉及兩種,那就是磁盤和 SSD。網絡

磁盤使用磁性元件作成盤片,而後使用盤片上對應位置是否有磁性,來判斷該位置存儲的值是邏輯 1 仍是邏輯 0。
另外一種是固態硬盤,也就是 SSD。SSD 的原理其實就是咱們的 「U盤」。從存儲介質的角度,「U盤」 其實不是一個準確的說法,準確的說,應該叫作 「閃存」(flash memory)。oop

從製造原理上,閃存又分爲兩種,一種是 NOR-flash,另外一種是 NAND-Flash。NOR-Flash 不會在服務器上使用,正文略過不講(感興趣的話,能夠看本文的小知識)。本文關注的是使用 NAND-Flash 製做而成的硬盤,也就是 SSD性能

硬盤 「塊設備」 是什麼?

Linux 中,全部的東西均可以抽象成文件。而全部的設備,則會被分爲字符設備和塊設備。
字符設備的意思是,對於該設備上隨機指定的地址的值,均可以直接寫入。學習

相對應地,塊設備的意思是,對於該設備上隨機指定的地址上的數據,若是須要修改的話,須要連同該地址周邊一整塊數據都一塊兒讀到內存中、修改了指定數據以後,再整塊寫回。優化

<<<< 小知識:爲何 SSD 是塊設備?爲何寫入這麼麻煩?>>>>

    SSD 不能簡單隨機地讀寫給定地址上的數據。SSD 讀取以 」頁「 爲單位(如:256 Bytes),擦除 / 
寫入以 」塊「 (與內存映射的 「4kB」 的那個 「塊」 不是一回事) 爲單位。

    此外,SSD 還有一個很大的特色,就是修改對應數據的時候,還不是簡單地執行一個 write 動做
(不是標準 C 的 write())就能夠了,而是須要首先執行一次 erase 操做,將當前的整個塊擦除掉
(所有變成邏輯 0),而後再將整個塊的數據從新寫一遍。

    因此對於塊設備而言,執行讀操做還算簡單,可是執行寫操做的話,整個流程就變成了:read、erase、
write 了,何其複雜!SSD 之因此被設計成這麼複雜的緣由,主要是半導體比特密度和制形成本之間的一個
取捨。原理能夠寫一整章,本文就不展開講了。

    爲何磁盤是塊設備?這個很抱歉,筆者沒有準確的答案。或許由於磁盤轉的太快,沒辦法極其精
準地定位具體一個 bit?

磁盤尋址速度的考量

這裏咱們先放下 SSD,來說一講磁盤。我的電腦和服務器上所使用的磁盤結構都是如出一轍的。磁盤的結構和各術語以下圖所示:

磁盤中會並列地擺放不少片盤片,每一個盤片的正反面都會有一個磁頭,使用這個磁頭來讀取盤片上的磁狀態。讀數據的時候,通常會同時從幾個盤片讀,以達到最高的讀取速度。

因此咱們能夠看到,磁盤尋址的速度,主要受到如下兩個因素的影響:

  • 磁頭轉動到指定位置的時間
  • 盤片旋轉,並將指定點轉動到磁頭下的時間

極端狀況下,磁頭須要從一端轉到另外一端;而盤片則須要轉動 180 度。

乍一看,其實這兩個時間是很是短的,彷佛並不會影響讀取文件的速度。兩邊引發質變,這兩個時間積累起來的時候,就會出現問題了。那麼怎樣才能累積呢?答案就是:頻繁、隨機地存取文件系統。

因此,提升磁盤效率的思路就是:避免頻繁的隨機存取文件

硬盤文件存取速度的考量

這裏所說的包含磁盤和 SSD。前面已經說了,硬盤是塊設備,也就是當讀取內容的時候,只能以塊爲單位進行操做。

這裏舉一個最簡單的例子吧,咱們把塊的單位減小到 8bits,存取的單位以 bits 來計算。好比在一段連續的內存中,數據是這樣的:

Addr:  0 1 2 3 4 5 6 7
value: 0 0 1 1 0 1 0 0

咱們只須要讀取地址 5 上的值,這就是所謂的隨機讀取。可是硬盤的原理決定了咱們沒辦法只讀這麼一個數據。驅動只能把這一整塊的數據(00110100)一塊兒讀出來,而後再把這地址 5 的值返回給應用程序。

寫入的時候就更復雜了。針對 SSD,若是咱們須要把地址 5 上的值改成 0,那麼驅動須要幹下面幾件事情:

  1. read: 把 [00110100] 讀到內存中
  2. mod: 將地址 5 的值在內存中設置爲 0,總共變成 [00110000]
  3. erase: 將硬盤中這一整塊的存儲內容擦除掉,擦除以後,這段地址的值會變成 [11111111](其實 SSD 的擦除動做仍是比較快的)
  4. write: 將實際須要設置的值 [00110000] 寫入到硬盤中。

磁盤還算比較簡單,只須要執行 1 和 4 兩個步驟就好了。

從上面的流程咱們大概能夠看到磁盤文件存取速度的優化方向了。若是你還沒弄明白的話,我再舉一個例子:

假設仍是上面的多個 SSD 塊,咱們作一次循環操做來模擬屢次存取。僞代碼以下:

define SECT_SIZE        (5)

int addr = 0;

for (sectNum  = 0 to 100)
{
    bits[SECT_SIZE] = read_data(from: addr, len: SECT_SIZE);

    bits[0] = 0;
    bits[SECT_SIZE - 1] = 0;    // 只修改一部分
    write_data(data: bits, from: addr, len: SECT_SIZE)
    
    addr += SECT_SIZE
}

這段代碼有什麼問題呢?咱們只須要取前兩次循環,看看驅動作了什事情就知道了。
首先是第一次循環(Loop 0):

此時 addr = 0,
1. 從 0 號塊讀出 8 bits 的數據,而且取其中的 5bits 返回給應用程序(耗時 t1)
2. 應用程序修改了兩個 bits
3. 將 0 號塊的 8bits 寫回(擦除 + 寫入,耗時 t2)

第二次循環就不同了:

此時 addr = 5
1. 從 0 號塊取出 8bits 的數據,而且取最後 3bits 爲有效數據(耗時 t1)
2. 從 1 號塊取出 8bits 的數據,而且取前 2bits 爲有效數據,與前面的 3bits 拼在一塊兒,返回 5bits 給應用程序(耗時 t1)
3. 應用程序修改了兩個 bits
4. 將 0 號塊的 8bits 寫回(耗時 t2)
5. 將 1 號塊的 8bits 寫回(耗時 t2)
總耗時是第一個循環的兩倍。

可見,因爲橫跨了兩個塊,雖然應用程序操做的數據量是同樣的,可是操做的時間卻多出了整整一倍。

所以從這裏咱們能夠看出,提升硬盤存取效率的另外一個途徑是:儘量以塊爲單位,進行讀寫操做。在操做系統中,軟件層面關心的塊的單位通常是 4096 字節(4kB)。

關於 NOR Flash

一句話,NOR Flash 只用在嵌入式設備中,作服務器開發的不須要關心。本小節沒有正片,只有小知識。

<<<< 小知識:NOR Flash 是什麼?有什麼特色? >>>>

    NOR Flash 和本文所說的 SDD(NAND Flash)的區別,首先在硬件結構上顯然是不一樣的。可是嘛,
如前文所述,硬件的咱不講。

    NOR Flash 的特色有下面幾點:
    1. NOR Flash 也是塊設備,但 NOR Flash 支持絕對的隨機讀取,要讀多長就讀多長,而且讀取
       速度高於 NAND Flash
    2. 相同條件下,NOR Flash 的擦除和寫入耗時遠遠大於 NAND Flash
    3. NOR Flash 支持有限的隨機寫能力,所謂有限,意思是 NOR Flash 能夠將任意邏輯 0 的位改
       寫爲邏輯 1,可是卻沒辦法將 1 改寫爲 0
    4. NOR 執行塊的擦除操做後,整個塊都會變成邏輯 0。所以對於絕大部分數據(0 和 1 混雜的)
       寫入操做,實際上和 NAND Flash 的操做流程是同樣的
    5. 當容量小於 16MB 時,NOR Flash 的成本小於 NAND Flash。再往上就不如 NAND 了。

    上面的特性,使得 NOR Flash 幾乎不會用在 PC 的存儲體系中。但卻在嵌入式設備中(包括計算
機主板上 BIOS 的存儲器)應用極廣,由於:
    1. 嵌入式設備主要是保存大量的代碼數據(只讀,特色1),和少數而且不多變更的用戶數據(讀寫,
       特色1)
    2. 嵌入式設備的程序空間很小,裁減過的 Linux 內核通過壓縮能夠達到2MB 甚至更低(特色5)
    3. 嵌入式設備可能隨時斷電,支持 jffs2 文件系統以後,能夠保護文件內容(特色3)
<<<< 小知識:爲何支持了 jffs2 就支持斷電保護?>>>>

    能夠說 jffs2 簡直就是爲 NOR Flash 量身定作的文件系統了。通常而言,好比咱們使用 FAT32 
對 U盤進行格式化以後,若是 U盤在寫入數據的時候忽然從主機設備上拔掉,那麼文件系統極可能會崩
潰、損壞。這就是爲何操做系統會有 「安全移除存儲設備」 的功能。

    Jffs2 完美地利用了 NOR Flash 「能夠將 0 改寫爲 1」 的特性。首先在格式化的時候,jffs2 以
塊爲單位劃分存儲空間,而後在建立索引表的時候。文件系統在將數據寫完以前,會對應的塊設置爲 0,
也就是標記爲無效塊。當數據徹底寫入完成以後,就將標記從 0 修改成 1。而 jffs2 每次加載的時候,
會遍歷全部的塊,而且在內存中重建索引表。

    若是在寫入數據的過程當中斷電了,那麼相應的塊繼續保持無效狀態。這樣就保證了文件系統的斷電
保護。
    固然 jffs2 的原理遠遠沒有這麼簡單。Linux 裏面就有 jffs2 的代碼,各位看官能夠自行取用。

小結

針對硬盤做爲塊設備的各類特色,從硬件原理上優化磁盤效率,咱們的思路主要就是兩個方向:

  1. 避免或者儘量減小存取磁盤上隨機地址的數據,也就是儘可能順序地存取文件
  2. 存取文件時儘量以塊爲單位存取(2 的倍數,最好是 4kB 的倍數)

實際代碼中具體應該怎麼操做,在下一篇文中給出。

參考資料

Nor/Nand FLASH的讀寫
讓 CPU 告訴你硬盤和網絡到底有多慢

相關文章
相關標籤/搜索