如果你還不知道Linux的髒頁回寫時機,那麼就看看VFS和PageCache

如果你還不知道Linux的髒頁回寫時機,那麼就看看VFS和PageCache

編者注:在分析完 Linux inode 基礎概念 之後,讓我們看下inode在內存中對應的文件系統抽象VFS,然後分析下關於 磁盤操作 中Page Cache的回寫策略。

VFS(虛擬文件系統層)

VFS是虛擬文件系統層(進程與文件系統之間的抽象層),與它相關的數據結構只存在於物理內存當中。其目的是屏蔽下層具體文件系統操作的差異,為上層的操作提供一個統一接口,正是由於VFS的存在,Linux中允許多個不同的文件系統共存。

VFS中包含著向物理文件系統轉換的一系列數據結構,如VFS超級塊、VFS的Inode、各種操作函數的轉換入口等。Linux中VFS依靠四個主要的數據結構來描述其結構信息,分別為超級塊、索引結點、目錄項和文件對象,這些數據結構大都會與磁盤上的對應上。

  • 超級塊(Super Block):超級塊對象表示一個文件系統。它存儲一個已安裝的文件系統的控制信息,包括文件系統名稱(比如Ext2)、文件系統的大小和狀態、塊設備的引用和元數據信息(比如空閒列表等等)。超級塊與磁盤上文件系統的超級塊對應。
  • 索引結點(Inode):索引結點對象存儲了文件的相關元數據信息,例如:文件大小、設備標識符、用戶標識符、用戶組標識符等等。Inode分為兩種:一種是VFS的Inode,一種是具體文件系統的Inode。前者在內存中,後者在磁盤中。所以每次其實是將磁盤中的Inode調進填充內存中的Inode,這樣才是算使用了磁盤文件Inode。當創建一個文件的時候,就給文件分配了一個Inode。一個Inode只對應一個實際文件,一個文件也會只有一個Inode(Unix/Linux系統中目錄也是一種文件,打開目錄實際上就是打開目錄文件。目錄文件的結構非常簡單,就是一系列目錄項(dirent)的列表。每個目錄項,由兩部分組成:所包含文件的文件名,以及該文件名對應的inode號碼)。
  • 目錄項(Dentry):引入目錄項對象的概念主要是出於方便查找文件的目的。不同於前面的兩個對象,目錄項對象只存在於內存中,實際對應的是磁盤的目錄innode對象。VFS在查找的時候,根據一層一層的目錄項找到對應的每個目錄項的Inode,那麼沿著目錄項進行操作就可以找到最終的文件。
  • 文件對象(File):文件對象描述的是進程已經打開的文件。因為一個文件可以被多個進程打開,所以一個文件可以存在多個文件對象,但多個文件對象其對應的索引節點和目錄項對象肯定是唯一的,關係如下圖:
  • 如果你還不知道Linux的髒頁回寫時機,那麼就看看VFS和PageCache

    由於進程中File對象有獨立的文件偏移量(current file offset),因此多個進程可以讀寫文件的不同位置的數據,但是一般不建議這樣玩,因為系統不保證該情況下的寫的原子性,多進程可以通過文件鎖實現對文件內容的寫保護。

    PageCache

    Page cache是通過將磁盤中的數據緩存到內存中,從而減少磁盤I/O操作,從而提高性能。此外,還要確保在page cache中的數據更改時能夠被同步到磁盤上,後者被稱為page回寫(page writeback)。一個inode對應一個page cache對象,一個page cache對象包含多個物理page。

    當內核發起一個讀請求時(例如進程發起read()請求),首先會檢查請求的數據是否緩存到了page cache中,如果有,那麼直接從內存中讀取,不需要訪問磁盤,這被稱為cache命中(cache hit)。如果cache中沒有請求的數據,即cache未命中(cache miss),就必須從磁盤中讀取數據。然後內核將讀取的數據緩存到cache中,這樣後續的讀請求就可以命中cache了。page可以只緩存一個文件部分的內容,不需要把整個文件都緩存進來。

    當內核發起一個寫請求時(例如進程發起write()請求),同樣是直接往cache中寫入,此時不會立即同步到磁盤,而是將寫入的page設置為髒頁,並將其加入dirty list中,內核會負責定期同步到磁盤保持二者一執行。

    page cache另一個主要工作是回收page釋放內存空間,此時會選擇合適的page進行釋放,如果是髒頁會先同步到磁盤然後釋放。此時是如何選擇cache頁的呢?Linux使用的策略是基於LRU改進的Two-List策略:

    Two-List策略維護了兩個list,active list 和 inactive list。在active list上的page被認為是hot的,不能釋放。只有inactive list上的page可以被釋放的。首次緩存的數據的page會被加入到inactive list中,已經在inactive list中的page如果再次被訪問,就會移入active list中。兩個鏈表都使用了偽LRU算法維護,新的page從尾部加入,移除時從頭部移除,就像隊列一樣。如果active list中page的數量遠大於inactive list,那麼active list頭部的頁面會被移入inactive list中,從而位置兩個表的平衡。

    觸發髒頁回寫到磁盤時機如下:

    • 用戶進程調用sync() 和 fsync()系統調用;
    • 空閒內存低於特定的閾值(threshold);
    • Dirty數據在內存中駐留的時間超過一個特定的閾值。

    注意這裡的page cache的髒頁回寫機制可以和mmap的髒頁回寫機制做下對比,mmap會在一定時間後系統自動回寫髒頁面到磁盤,也就是說mamp中修改過的髒頁面並不會立即更新迴文件中,而是有一段時間的延遲,可以調用msync()來強制同步, 這樣所寫的內容就能立即保存到文件裡了。

    推薦閱讀:


    分享到:


    相關文章: