頁緩存是Linux內核一種重要的磁盤高速緩存,它通過軟件機制實現。但頁緩存和硬件cache的原理基本相同,將容量大而低速設備中的部分數據存放到容量小而快速的設備中,這樣速度快的設備將作為低速設備的緩存,當訪問低速設備中的數據時,可以直接從緩存中獲取數據而不需再訪問低速設備,從而節省了整體的訪問時間。
頁緩存以頁為大小進行數據緩存,它將磁盤中最常用和最重要的數據存放到部分物理內存中,使得系統訪問塊設備時可以直接從主存中獲取塊設備數據,而不需從磁盤中獲取數據。
在大多數情況下,內核在讀寫磁盤時都會使用頁緩存。內核在讀文件時,首先在已有的頁緩存中查找所讀取的數據是否已經存在。如果該頁緩存不存在,則一個新的頁將被添加到高速緩存中,然後用從磁盤讀取的數據填充它。如果當前物理內存足夠空閒,那麼該頁將長期保留在高速緩存中,使得其他進程再使用該頁中的數據時不再訪問磁盤。寫操作與讀操作時類似,直接在頁緩存中修改數據,但是頁緩存中修改的數據(該頁此時被稱為Dirty Page)並不是馬上就被寫入磁盤,而是延遲幾秒鐘,以防止進程對該頁緩存中的數據再次修改。
頁緩存的設計需求
頁緩存至少需要滿足以下兩種需求。首先,它必須可以快速定位含有給定數據的特定頁。其次,由於頁高速緩存中的數據來源不同,比如普通文件、塊設備等,內核必須根據不同的數據來源來選擇對頁緩存的適當操作。
內核通過抽象出address_space數據結構來滿足上述兩種設計需求。
address_space結構
address_space 結構是頁高速緩存機制中的核心數據結構,該結構並不是對某一個頁高速緩存進行描述,而是以頁高速緩存的所有者(owner)為單位,對其所擁有的緩存進行抽象描述。頁高速緩存中每個頁包含的數據肯定屬於某個文件,該文件對應的inode對象就稱為頁高速緩存的所有者。
頁緩存與文件系統和內存管理都有聯系。每個inode結構中都嵌套一個address_space結構,即inode字段中的i_data;同時inode中還有 i_maping字段指向所嵌套address_spaces結構。而address_space結構通過host字段反指向頁高速緩存的所有者。頁緩存的本質就是一個物理頁框,因此每個頁描述符中通過mmaping和index兩個字段與高速緩存進行關聯。mmaping指向頁緩存所有者中的 address_space對象。index表示以頁大小為單位的偏移量,該偏移量表示頁框內數據在磁盤文件中的偏移量。
address_space 結構中的i_mmap字段指向一個radix優先搜索樹。該樹將一個文件所有者中的所有頁緩存組織在一起,這樣可以快速搜索到指定的頁緩存。內核中關於 radix樹有一套標准的使用方法,它不與特定的數據聯系(與內核雙聯表類似),這樣使得使用范圍更加靈活。具體操作如下:
radix_tree_lookup():在radix樹中對指定節點進行查找;
radix_tree_insert():在radix樹中插入新節點;
radix_tree_delete():在radix樹中刪除指定節點;
此外,該結構中的a_ops字段指向address_space_operations結構,該結構是一個鉤子函數集,它表明了對所有者的頁進行操作的標准方法。比如writepage鉤子函數表示將頁中的數據寫入到磁盤中,readpage表示從磁盤文件中讀數據到頁中。通常,這些鉤子函數將頁緩存的所有者(inode)和訪問物理設備的低級驅動程序關聯起來。該函數集使得內核在上層使用統一的接口與頁緩存進行交互,而底層則根據頁緩存中數據的來源具體實現。
通過上面的描述,可以看到address_space結構中的優先搜索樹和鉤子函數集解決了頁高速緩存的兩個主要設計需求。
內核對頁緩存的操作函數
內核對頁緩存的基本操作包含了在一個頁緩存所形成的radix樹中查找,增加和刪除一個頁緩存。基於radix的基本操作函數,頁高速緩存的處理函數如下:
page_cache_alloc():分配一個新的頁緩存;
find_get_page():在頁高速緩存中查找指定頁;
add_to_page_cache():把一個新頁添加到頁高速緩存;
remove_from_page_cache():將指定頁從頁高速緩存中移除;
read_cache_page():確保指定頁在頁高速緩存中包含最新的數據;
參考:
1. 深入理解Linux內核 第三版
2.深入Linux內核架構