Linux 内核的文件 Cache 管理机制介绍
1 前言
自從誕生以來,Linux 就被不斷完善和普及,目前它已經成為主流通用操作系統之一,使用得非常廣泛,它與 Windows、UNIX 一起占據了操作系統領域幾乎所有的市場份額。特別是在高性能計算領域,Linux 已經成為一個占主導地位的操作系統,在2005年6月全球TOP500 計算機中,有 301 臺部署的是 Linux 操作系統。因此,研究和使用 Linux 已經成為開發者的不可回避的問題了。
下面我們介紹一下 Linux 內核中文件 Cache 管理的機制。本文以 2.6 系列內核為基準,主要講述工作原理、數據結構和算法,不涉及具體代碼。
回頁首
2 操作系統和文件 Cache 管理
操作系統是計算機上最重要的系統軟件,它負責管理各種物理資源,并向應用程序提供各種抽象接口以便其使用這些物理資源。從應用程序的角度看,操作系統提供了一個統一的虛擬機,在該虛擬機中沒有各種機器的具體細節,只有進程、文件、地址空間以及進程間通信等邏輯概念。這種抽象虛擬機使得應用程序的開發變得相對容易:開發者只需與虛擬機中的各種邏輯對象交互,而不需要了解各種機器的具體細節。此外,這些抽象的邏輯對象使得操作系統能夠很容易隔離并保護各個應用程序。
對于存儲設備上的數據,操作系統向應用程序提供的邏輯概念就是"文件"。應用程序要存儲或訪問數據時,只需讀或者寫"文件"的一維地址空間即可,而這個地址空間與存儲設備上存儲塊之間的對應關系則由操作系統維護。
在 Linux 操作系統中,當應用程序需要讀取文件中的數據時,操作系統先分配一些內存,將數據從存儲設備讀入到這些內存中,然后再將數據分發給應用程序;當需要往文件中寫數據時,操作系統先分配內存接收用戶數據,然后再將數據從內存寫到磁盤上。文件 Cache 管理指的就是對這些由操作系統分配,并用來存儲文件數據的內存的管理。Cache 管理的優劣通過兩個指標衡量:一是 Cache 命中率,Cache 命中時數據可以直接從內存中獲取,不再需要訪問低速外設,因而可以顯著提高性能;二是有效 Cache 的比率,有效 Cache 是指真正會被訪問到的 Cache 項,如果有效 Cache 的比率偏低,則相當部分磁盤帶寬會被浪費到讀取無用 Cache 上,而且無用 Cache 會間接導致系統內存緊張,最后可能會嚴重影響性能。
下面分別介紹文件 Cache 管理在 Linux 操作系統中的地位和作用、Linux 中文件 Cache相關的數據結構、Linux 中文件 Cache 的預讀和替換、Linux 中文件 Cache 相關 API 及其實現。
回頁首
2 文件 Cache 的地位和作用
文件 Cache 是文件數據在內存中的副本,因此文件 Cache 管理與內存管理系統和文件系統都相關:一方面文件 Cache 作為物理內存的一部分,需要參與物理內存的分配回收過程,另一方面文件 Cache 中的數據來源于存儲設備上的文件,需要通過文件系統與存儲設備進行讀寫交互。從操作系統的角度考慮,文件 Cache 可以看做是內存管理系統與文件系統之間的聯系紐帶。因此,文件 Cache 管理是操作系統的一個重要組成部分,它的性能直接影響著文件系統和內存管理系統的性能。
圖1描述了 Linux 操作系統中文件 Cache 管理與內存管理以及文件系統的關系示意圖。從圖中可以看到,在 Linux 中,具體文件系統,如 ext2/ext3、jfs、ntfs 等,負責在文件 Cache和存儲設備之間交換數據,位于具體文件系統之上的虛擬文件系統VFS負責在應用程序和文件 Cache 之間通過 read/write 等接口交換數據,而內存管理系統負責文件 Cache 的分配和回收,同時虛擬內存管理系統(VMM)則允許應用程序和文件 Cache 之間通過 memory map的方式交換數據。可見,在 Linux 系統中,文件 Cache 是內存管理系統、文件系統以及應用程序之間的一個聯系樞紐。
回頁首
3 文件 Cache 相關數據結構
在 Linux 的實現中,文件 Cache 分為兩個層面,一是 Page Cache,另一個 Buffer Cache,每一個 Page Cache 包含若干 Buffer Cache。內存管理系統和 VFS 只與 Page Cache 交互,內存管理系統負責維護每項 Page Cache 的分配和回收,同時在使用 memory map 方式訪問時負責建立映射;VFS 負責 Page Cache 與用戶空間的數據交換。而具體文件系統則一般只與 Buffer Cache 交互,它們負責在外圍存儲設備和 Buffer Cache 之間交換數據。Page Cache、Buffer Cache、文件以及磁盤之間的關系如圖 2 所示,Page 結構和 buffer_head 數據結構的關系如圖 3 所示。在上述兩個圖中,假定了 Page 的大小是 4K,磁盤塊的大小是 1K。本文所講述的,主要是指對 Page Cache 的管理。
在 Linux 內核中,文件的每個數據塊最多只能對應一個 Page Cache 項,它通過兩個數據結構來管理這些 Cache 項,一個是 radix tree,另一個是雙向鏈表。Radix tree 是一種搜索樹,Linux 內核利用這個數據結構來通過文件內偏移快速定位 Cache 項,圖 4 是 radix tree的一個示意圖,該 radix tree 的分叉為4(22),樹高為4,用來快速定位8位文件內偏移。Linux(2.6.7) 內核中的分叉為 64(26),樹高為 6(64位系統)或者 11(32位系統),用來快速定位 32 位或者 64 位偏移,radix tree 中的每一個葉子節點指向文件內相應偏移所對應的Cache項。
另一個數據結構是雙向鏈表,Linux內核為每一片物理內存區域(zone)維護active_list和inactive_list兩個雙向鏈表,這兩個list主要用來實現物理內存的回收。這兩個鏈表上除了文件Cache之外,還包括其它匿名(Anonymous)內存,如進程堆棧等。
回頁首
4 文件Cache的預讀和替換
Linux內核中文件預讀算法的具體過程是這樣的:對于每個文件的第一個讀請求,系統讀入所請求的頁面并讀入緊隨其后的少數幾個頁面(不少于一個頁面,通常是三個頁面),這時的預讀稱為同步預讀。對于第二次讀請求,如果所讀頁面不在Cache中,即不在前次預讀的group中,則表明文件訪問不是順序訪問,系統繼續采用同步預讀;如果所讀頁面在Cache中,則表明前次預讀命中,操作系統把預讀group擴大一倍,并讓底層文件系統讀入group中剩下尚不在Cache中的文件數據塊,這時的預讀稱為異步預讀。無論第二次讀請求是否命中,系統都要更新當前預讀group的大小。此外,系統中定義了一個window,它包括前一次預讀的group和本次預讀的group。任何接下來的讀請求都會處于兩種情況之一:第一種情況是所請求的頁面處于預讀window中,這時繼續進行異步預讀并更新相應的window和group;第二種情況是所請求的頁面處于預讀window之外,這時系統就要進行同步預讀并重置相應的window和group。圖5是Linux內核預讀機制的一個示意圖,其中a是某次讀操作之前的情況,b是讀操作所請求頁面不在window中的情況,而c是讀操作所請求頁面在window中的情況。
Linux內核中文件Cache替換的具體過程是這樣的:剛剛分配的Cache項鏈入到inactive_list頭部,并將其狀態設置為active,當內存不夠需要回收Cache時,系統首先從尾部開始反向掃描active_list并將狀態不是referenced的項鏈入到inactive_list的頭部,然后系統反向掃描inactive_list,如果所掃描的項的處于合適的狀態就回收該項,直到回收了足夠數目的Cache項。Cache替換算法如圖6的算法描述偽碼所示。
圖6 Linux的Cache替換算法描述
Mark_Accessed(b) {if b.state==(UNACTIVE && UNREFERENCE) b.state = REFERENCEelse if b.state == (UNACTIVE && REFERENCE) {b.state = (ACTIVE && UNREFERENCE)Add X to tail of active_list} else if b.state == (ACTIVE && UNREFERENCE) b.state = (ACTIVE && REFERENCE) } Reclaim() {if active_list not empty and scan_num<MAX_SCAN1{X = head of active_listif (X.state & REFERENCE) == 0Add X to tail of inactive_listelse {X.state &= ~REFERENCE Move X to tail of active_list}scan_num++}scan_num = 0if inactive_list not emptry and scan_num <MAX_SCAN2 {X = head of inactive_list if (X.state & REFERENCE) == 0return Xelse {X.state = ACTIVE | UNREFERENCE Move X to tail of active_list}scan_num++}return NULL } Access(b){if b is not in cache {if slot X freeput b into Xelse {X=Reclaim()put b into X}Add X to tail of inactive_list}Mark_Accessed(X) }回頁首
5 文件Cache相關API及其實現
Linux內核中與文件Cache操作相關的API有很多,按其使用方式可以分成兩類:一類是以拷貝方式操作的相關接口, 如read/write/sendfile等,其中sendfile在2.6系列的內核中已經不再支持;另一類是以地址映射方式操作的相關接口,如mmap等。
第一種類型的API在不同文件的Cache之間或者Cache與應用程序所提供的用戶空間buffer之間拷貝數據,其實現原理如圖7所示。
第二種類型的API將Cache項映射到用戶空間,使得應用程序可以像使用內存指針一樣訪問文件,Memory map訪問Cache的方式在內核中是采用請求頁面機制實現的,其工作過程如圖8所示。
首先,應用程序調用mmap(圖中1),陷入到內核中后調用do_mmap_pgoff(圖中2)。該函數從應用程序的地址空間中分配一段區域作為映射的內存地址,并使用一個VMA(vm_area_struct)結構代表該區域,之后就返回到應用程序(圖中3)。當應用程序訪問mmap所返回的地址指針時(圖中4),由于虛實映射尚未建立,會觸發缺頁中斷(圖中5)。之后系統會調用缺頁中斷處理函數(圖中6),在缺頁中斷處理函數中,內核通過相應區域的VMA結構判斷出該區域屬于文件映射,于是調用具體文件系統的接口讀入相應的Page Cache項(圖中7、8、9),并填寫相應的虛實映射表。經過這些步驟之后,應用程序就可以正常訪問相應的內存區域了。
回頁首
6 小結
文件Cache管理是Linux操作系統的一個重要組成部分,同時也是研究領域一個很熱門的研究方向。目前,Linux內核在這個方面的工作集中在開發更有效的Cache替換算法上,如LIRS(其變種ClockPro)、ARC等。相關信息可見http://linux-mm.org/AdvancedPageReplacement。
參考資料
- Understanding Linux Kernel Edition2
- Linux內核源代碼情景分析
- http://www.top500.org
- R.W.Carr and J.L.Hennessy. WSCLOCK - a simple and effective algorithm for virtual mem-ory management. In Proc. ACM SOSP-08, Dec 1981.
- S. Jiang, F. Chen, and X. Zhang. CLOCK-Pro: An effective improvement of the CLOCK repla-cement. In Proc. USENIX ATC, Apr. 2005.
- S. Jiang and X. Zhang. LIRS: an efficient low in-terreference set replacement policy to improve buffer cache performance. In Proc. ACM SIG-METRICS, June 2002.
- N. Megiddo and D. S. Modha. ARC: A Self-tun-ing, Low Overhead Replacement Cache. In Proc. 2nd USENIX FAST, Mar 2003.?
原文地址: ?https://www.ibm.com/developerworks/cn/linux/l-cache/
總結
以上是生活随笔為你收集整理的Linux 内核的文件 Cache 管理机制介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入FFM原理与实践
- 下一篇: Linux系统中,read文件过程分析