Linux下的磁碟緩衝

來源:互聯網
上載者:User

前段時間在開發一個使用SSD做緩衝的系統,在高速寫入資料時會出現大量的磁碟緩衝。太多的磁碟緩衝如果沒有及時的寫入磁碟中,在機器出現問題時是非常危險的,這樣會導致很多的資料丟失,但是如果即時的將資料刷入磁碟中,這樣寫入效率有太低了。為了弄明白Linux系統的這種磁碟寫入特性,最近深入的學習了一下。

VFS(Virtual File System)的存在使得Linux可以相容不同的檔案系統,例如ext3、ext4、xfs、ntfs等等,其不僅具有為所有的檔案系統實現一個通用的外介面的作用,還具有另一個與系統效能相關的重要作用——緩衝。VFS中引入了磁碟快取的機制,這屬於一種軟體機制,允許核心將原本存在磁碟上的某些資訊儲存在RAM中,以便對這些資料的進一步訪問能快速進行,而不必慢速訪問磁碟本身。磁碟快取可大致分為以下三種:

  1. 目錄項快取——主要存放的是描述檔案系統路徑名的目錄項對象
  2. 索引節點快取——主要存放的是描述磁碟索引節點的索引節點對象
  3. 頁快取——主要存放的是完整的資料頁對象,每個頁所包含的資料一定屬於某個檔案,同時,所有的檔案讀寫操作都依賴於頁快取。其是Linux核心所使用的主要磁碟快取。

正是由於緩衝的引入,所以VFS檔案系統採用了檔案資料延遲寫的技術,因此,如果在調用系統介面寫入資料時沒有使用同步寫入模式,那麼大多資料將會先儲存在緩衝中,待等到滿足某些條件時才將資料刷入磁碟裡。

核心是如何將資料刷入磁碟的呢?在看完以下兩點後就能得到答案。

1. 把髒頁寫入磁碟

正如我們所瞭解的,核心不斷用包含塊裝置資料的頁填充頁快取。只要進程修改了資料,相應的頁就被標記為髒頁,即把它的PG_dirty標誌位置。

Unix系統允許把髒緩衝區寫入塊裝置的操作順延強制,因為這種策略可以顯著地提高系統的效能。對快取中的頁的幾次寫操作可能只需對相應的磁碟塊進行一次緩慢的物理更新就可以滿足。此外,寫操作沒有讀操作那麼緊迫,因為進程通常是不會因為延遲寫而掛起,而大部分情況都因為延遲讀而掛起。正是由於延遲寫,使得任一物理塊裝置平均為讀請求提供服務將多於寫請求。

一個髒頁可能直到最後一刻(即直到系統關閉時)都一直逗留在主存中。然而,從延遲寫策略的局限性來看,它有兩個主要的缺點:

一、如果發生了硬體錯誤或者電源掉電的情況,那麼就無法再獲得RAM的內容,因此,從系統啟動以來對檔案進行的很多修改就丟失了。

二、頁快取的大小(由此存放它所需的RAM的大小)就可要很大——至少要與所訪問塊裝置的大小不同。

因此,在下列條件下把髒頁重新整理(寫入)到磁碟:

  1. 頁快取變得太滿,但還需要更多的頁,或者髒頁的數量已經太多。
  2. 自從頁變成髒頁以來已過去太長時間。
  3. 進程請求對塊裝置或者特定檔案任何待定的變化都進行重新整理。通過調用sync()、fsync()或者fdatasync()系統調用來實現。

緩衝區頁的引入是問題更加複雜。與每個緩衝區頁相關的緩衝區首部使核心能夠瞭解每個獨立塊緩衝區的狀態。如果至少有一個緩衝區首部的PG_Dirty標誌被置位,就應該設定相應緩衝區頁的PG_dirty標誌。當核心選擇要重新整理的緩衝區時,它掃描相應的緩衝區首部,並只把髒塊的內容有效寫到磁碟。一旦核心把緩衝區的所有髒頁重新整理到磁碟,就把頁的PG_dirty標誌清0。

2. pdflush核心線程

早期版本的Linux使用bdfllush核心線程系統地掃描頁快取以搜尋要重新整理的髒頁,並且使用另一個核心線程kupdate來保證所有的頁不會“髒”太長時間。Linux 2.6用一組通用核心線程pdflush替代上述兩個線程。

這些核心線程結構靈活,它們作用於兩個參數:一個指向線程要執行的函數的指標和一個函數要用的參數。系統中pdflush核心線程的數量是要動態調整的:pdflush線程太少時就建立,太多時就殺死。因為這些核心線程所執行的函數可以阻塞,所以建立多個而不是一個pdflush核心線程可以改善系統效能。

根據下面的原則控制pdflush線程的產生和消亡:

  1. 必須有至少兩個,最多八個pdflush核心線程
  2. 如果到最近的1s期間沒有空閑pdflush,就應該建立新的pdflush線程
  3. 如果最近一次pdflush變為空白閑的時間超過了1s,就應該刪除一個pdflush線程

所有的pdflush核心線程都有pdflush_work描述符,其資料結構如下:

類型 欄位 說明
struct task_struct who 指向核心線程描述符的指標
void (*) (unsigned long) fn 核心線程所執行的回呼函數
unsigned long arg0 給回呼函數的參數
struct list head list pdflush_list鏈表的連結
unsigned long when_i_went_to_sleep 當核心線程可用時的時間(以jiffies表示)

當系統沒有要重新整理的髒頁時,pdflush線程會自動處於睡眠狀態,最後由pdflush_operation()函數來喚醒。那麼在這個過程中pdflush核心線程主要完成了哪些工作呢?其中一些工作與髒資料的重新整理有關。尤其是pdflush通常執行下面的回呼函數之一:

1. background_writeout(): 系統地掃描頁快取以搜尋要重新整理的髒頁。

為了得到需要重新整理的髒頁,就要徹底的搜尋與在磁碟上有映像的索引節點相應的所有address_space對象(是一棵搜尋樹)。由於頁快取可能有大量的頁,如果用一個單獨的執行流來掃描整個快取,會令CPU和磁碟長時間繁忙,因此,Linux使用一種複雜的機制把對頁快取的掃描劃分為幾個執行流。當記憶體不足或者使用者顯式的(使用者態進程發出sync()系統調用等)調用請求重新整理操作時會執行wakeup_bdflush()函數。wakeup_bdflush()函數會調用pdflush_operation()喚醒pdflush核心線程,並委託它執行回呼函數background_writeout()。background_writeout()函數有效從頁快取中獲得指定數量的髒頁,並把它寫回磁碟。此外,執行background_writeout()函數的pdflush核心線程只有在滿足以下兩個條件下才能被喚醒:一是對頁快取中的頁內容進行了修改,二是引起髒頁部分增加到超過某個髒背景閾值。背景閾值通常設定為系統中所有頁的10%,不過可以通過修改檔案/proc/sys/vm/dirty_background_ratio來調整該值。

2. wb_kupdate():檢查頁快取中是否有“髒”了很久時間的頁,避免當一些頁很久沒有被重新整理時發生饑餓危險。

核心在初始化期間會建立wb_timer動態定時器,其的定時間距為dirty_writeback_centisecs檔案中所規定的幾百分之一秒(通常是500分之一秒,不過可以通過修改/proc/sys/vm/dirty_writeback_centisecs檔案調整該值)。定時器函數會調用pdflush_operation()函數,然後將wb_kupdate()函數的地址傳入。wb_kupdate()函數遍曆頁快取搜尋陳舊的髒索引節點,把已保持髒狀態時間超過30秒的頁都寫到磁碟,之後重設定時器。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.