Page Cache與Page回寫

来源:https://www.cnblogs.com/linhaostudy/archive/2018/12/29/10196915.html
-Advertisement-
Play Games

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


綜述

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

對磁碟的數據進行緩存從而提高性能主要是基於兩個因素:第一,磁碟訪問的速度比記憶體慢好幾個數量級(毫秒和納秒的差距)。第二是被訪問過的數據,有很大概率會被再次訪問。

Page Cache

Page cache由記憶體中的物理page組成,其內容對應磁碟上的block。page cache的大小是動態變化的,可以擴大,也可以在記憶體不足時縮小。cache緩存的存儲設備被稱為後備存儲(backing store),註意我們在block I/O中提到的:一個page通常包含多個block,這些block不一定是連續的。

讀Cache

當內核發起一個讀請求時(例如進程發起read()請求),首先會檢查請求的數據是否緩存到了page cache中,如果有,那麼直接從記憶體中讀取,不需要訪問磁碟,這被稱為cache命中(cache hit)。

如果cache中沒有請求的數據,即cache未命中(cache miss),就必須從磁碟中讀取數據。然後內核將讀取的數據緩存到cache中,這樣後續的讀請求就可以命中cache了。page可以只緩存一個文件部分的內容,不需要把整個文件都緩存進來。

寫Cache

當內核發起一個寫請求時(例如進程發起write()請求),同樣是直接往cache中寫入,後備存儲中的內容不會直接更新。內核會將被寫入的page標記為dirty,並將其加入dirty list中。內核會周期性地將dirty list中的page寫回到磁碟上,從而使磁碟上的數據和記憶體中緩存的數據一致。

Cache回收

Page cache的另一個重要工作是釋放page,從而釋放記憶體空間。cache回收的任務是選擇合適的page釋放,並且如果page是dirty的,需要將page寫回到磁碟中再釋放。理想的做法是釋放距離下次訪問時間最久的page,但是很明顯,這是不現實的。下麵先介紹LRU演算法,然後介紹基於LRU改進的Two-List策略,後者是Linux使用的策略。

LRU演算法

LRU(least rencently used)演算法是選擇最近一次訪問時間最靠前的page,即幹掉最近沒被光顧過的page。原始LRU演算法存在的問題是,有些文件只會被訪問一次,但是按照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中,從而位置兩個表的平衡。

Page Cache在Linux中的具體實現

address_space結構

內核使用address_space結構來表示一個page cache,address_space這個名字起得很糟糕,叫page_ache_entity可能更合適。下麵是address_space的定義

struct address_space {
    struct inode            *host;              /* owning inode */
    struct radix_tree_root  page_tree;          /* radix tree of all pages */
    spinlock_t              tree_lock;          /* page_tree lock */
    unsigned int            i_mmap_writable;    /* VM_SHARED ma count */
    struct prio_tree_root   i_mmap;             /* list of all mappings */
    struct list_head        i_mmap_nonlinear;   /* VM_NONLINEAR ma list */
    spinlock_t              i_mmap_lock;        /* i_mmap lock */
    atomic_t                truncate_count;     /* truncate re count */
    unsigned long           nrpages;            /* total number of pages */
    pgoff_t                 writeback_index;    /* writeback start offset */
    struct address_space_operations *a_ops;     /* operations table */
    unsigned                long flags;         /* gfp_mask and error flags */
    struct backing_dev_info *backing_dev_info;  /* read-ahead information */
    spinlock_t              private_lock;       /* private lock */
    struct list_head        private_list;       /* private list */
    struct address_space    *assoc_mapping;     /* associated buffers */
};

其中 host域指向對應的inode對象,host有可能為NULL,這意味著這個address_space不是和一個文件關聯,而是和swap area相關,swap是Linux中將匿名記憶體(比如進程的堆、棧等,沒有一個文件作為back store)置換到swap area(比如swap分區)從而釋放物理記憶體的一種機制。page_tree保存了該page cache中所有的page,使用基數樹(radix Tree)來存儲。i_mmap是保存了所有映射到當前page cache(物理的)的虛擬記憶體區域(VMA)。nrpages是當前address_space中page的數量。

address_space操作函數

address_space中的a_ops域指向操作函數表(struct address_space_operations),每個後備存儲都要實現這個函數表,比如ext3文件系統在fs/ext3/inode.c中實現了這個函數表。

內核使用函數表中的函數管理page cache,其中最重要的兩個函數是readpage() 和writepage()

readpage()函數

readpage()首先會調用find_get_page(mapping, index)在page cache中尋找請求的數據,mapping是要尋找的page cache對象,即address_space對象,index是要讀取的數據在文件中的偏移量。如果請求的數據不在該page cache中,那麼內核就會創建一個新的page加入page cache中,並將要請求的磁碟數據緩存到該page中,同時將page返回給調用者。

writepage() 函數

對於文件映射(host指向一個inode對象),page每次修改後都會調用SetPageDirty(page)將page標識為dirty。(個人理解swap映射的page不需要dirty,是因為不需要考慮斷電丟失數據的問題,因為記憶體的數據斷電時預設就是會失去的)內核首先在指定的address_space尋找目標page,如果沒有,就分配一個page並加入到page cache中,然後內核發起一個寫請求將數據從用戶空間拷入內核空間,最後將數據寫入磁碟中。(對從用戶空間拷貝到內核空間不是很理解,後期會重點學習Linux讀、寫文件的詳細過程然後寫一篇詳細的blog介紹)

Buffer Cache

在Block I/O的文章中提到用於表示記憶體到磁碟映射的buffer_head結構,每個buffer-block映射都有一個buffer_head結構,buffer_head中的b_assoc_map指向了address_space。在Linux2.4中,buffer cache和 page cache之間是獨立的,前者使用老版本的buffer_head進行存儲,這導致了一個磁碟block可能在兩個cache中同時存在,造成了記憶體的浪費。2.6內核中將兩者合併到了一起,使buffer_head只存儲buffer-block的映射信息,不再存儲block的內容。這樣保證一個磁碟block在記憶體中只會有一個副本,減少了記憶體浪費。

Flusher線程群(Flusher Threads)

Page cache推遲了文件寫入後備存儲的時間,但是dirty page最終還是要被寫回磁碟的。

內核在下麵三種情況下會進行會將dirty page寫回磁碟:

  • 用戶進程調用sync() 和 fsync()系統調用
  • 空閑記憶體低於特定的閾值(threshold)
  • Dirty數據在記憶體中駐留的時間超過一個特定的閾值

線程群的特點是讓一個線程負責一個存儲設備(比如一個磁碟驅動器),多少個存儲設備就用多少個線程。這樣可以避免阻塞或者競爭的情況,提高效率。當空閑記憶體低於閾值時,內核就會調用wakeup_flusher_threads()來喚醒一個或者多個flusher線程,將數據寫回磁碟。為了避免dirty數據在記憶體中駐留過長時間(避免在系統崩潰時丟失過多數據),內核會定期喚醒一個flusher線程,將駐留時間過長的dirty數據寫回磁碟。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在用Easy UI+Dapper+MVC4 開發一個財務收款系統,其中就發現一些小問題,供有需要的人參考。 1.EasyUI控制項combobox 數據綁定 出現重覆請求後臺 上代碼: 當時真的好奇怪,這樣寫沒問題啊,可combobox卻重覆請求,於是在去看EasyUi 文檔http://www. ...
  • Highchars //前臺 <script> $(function () { //showChat(); initChat(); showPie(); initPie(); }) function initChat() { var xlst = []; var ylst = []; $.ajax( ...
  • C#操作SQL資料庫 Connection(連接)對象 形式2.”server=;Intergrated Security=true/SSPI” Command(命令)對象 形式2.new SqlCommand(Sql語句, 連接對象)//省略2,5 形式2.執行select語句(count,sum ...
  • 對象引擎,以路徑形式訪問對象屬性,例data.Product[1].Name。 在做excel模板引擎的時候,為了能方便的調用對象屬性,找了一些模板引擎,不是太大就是不太適用於excel, 因為excel模板中不太適用寫一些語法,所以要簡化,現把對象引擎這一塊簡化抽出來分享 開源地址:https:/ ...
  • 接觸Linux已經有一段時間了,由於實際需要,三三兩兩地掌握了一些基本語法和實用語句,主要都是在日常開發中用得比較多的,條理不是特別清晰,請見諒!下麵開始上硬貨!! 基本操作: 關閉Linux系統的命令:init 0 切換虛擬終端的命令:Ctrl + Alt + F[1~6] 釋:Linux共有6個 ...
  • 有關Linux ipv6模塊載入失敗的問題 同事一個SUSE11sp3環境配置ipv6地址失敗,提示不支持IPv6,請求幫助,第一反應是應該ipv6相關內核模塊沒有載入。 主要檢查內容: ipv6地址是否存在 ifconfig |grep inet6 沒有預設inet6地址 ipv6模塊是否存在 # ...
  • 小編最近想學習一下小程式的開發,然後發現小程式的開發需要功能變數名稱為Https協議,所以就特地去配置了一下,配置成功了就放出來和大家一起分享 ...
  • 網路上有很多關於優秀的關於Paxos 演算法的文章,我下麵進行整理搜集一下: 分散式理論之一:Paxos演算法的通俗理解 維基的簡介:Paxos演算法是萊斯利·蘭伯特(Leslie Lamport,就是 LaTeX 中的"La",此人現在在微軟研究院)於1990年提出的一種基於消息傳遞且具有高度容錯特性的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...