1、頁高速緩存(cache)
是LINUX內核實現的一種主要磁盤緩存。它主要用來減少對磁盤的I/O操作。具體而言,通過把磁盤的數據緩存到物理內存中,把對磁盤的訪問變為對物理內存的訪問。
2、頁高速緩存的價值
磁盤高速緩存的價值在兩方面:
-
訪問磁盤的速度遠低於訪問內存的速度;
-
數據一旦被訪問,就很有可能在短期內再次被訪問(時間局部性原理),這些數據會被暫存在高速緩存中,實現快速命中。
3、頁高速緩存的實現理論
頁高速緩存由RAM中的物理頁組成。緩存中每一頁對應着磁盤中的多個塊。
-
內核開始一個讀操作,首先檢查頁高速緩存中是否有該數據。
-
如果有,則直接讀取。這一步叫做緩存命中;如果沒有,則直接從磁盤讀寫,這叫未緩存命中。
-
內核調度塊I/O操作從磁盤中讀取數據,並將全部數據或者部分數據放入頁高速緩存中。注意:緩存誰是取決於誰被訪問到。系統不一定將文件中全部放入內存,可能只是部分內容。
舉個例子:
使用文本編輯器打開一個源程序時,該文件的數據就被調入內存。編輯該文件的過程中,越來越多的數據相繼被調入內存頁(空間局部性原理)。最后,當你編譯它的時候,內核可以直接使用頁高速緩存中的頁,而不需要重新從磁盤讀取該文件了。
4、頁高速緩存的意義
減少對磁盤的訪問,實現高速訪問數據。
5、頁高速緩存的機制有哪些?
頁高速緩存主要有3種機制來保證讀、寫緩存以及釋放緩存,分別如下:
-
讀緩存
-
寫緩存
-
緩存回收
讀緩在第3點已經有描述了,此處不做贅述。主要講下寫緩存和緩存回收。
5.1 寫緩存
內核需要從磁盤讀數據時,如果在頁高速緩存中沒有讀到數據,就會直接從磁盤中讀取數據並將數據存入頁高速緩存中,以保證下次命中。
而寫緩存在寫磁盤時會有3種策略:
(1)不緩存
這種做法也叫做直接I/O。
數據寫入磁盤時,不經過頁高速緩存,而是直接將數據寫入磁盤。
這種做法的缺點很明顯,如果下次需要讀這個數據時,因為頁高速緩存無此數據,因此需要直接從磁盤讀取,需要額外開銷。
因此該策略很少使用。
(2)寫透緩存
即寫操作更新內存緩存,同時也更新磁盤文件。
該策略可以保持緩存一致性——緩存數據的同時在后備存儲(磁盤)保持同步。
這個與不緩存策略比較有一點優勢就是,讀可以試圖先讀頁高速緩存。
(3)回寫
程序執行寫操作直接將數據寫到緩存中,然后將頁高速緩存中被寫入的頁面標記為“臟”,並且將這些“臟”頁面加入到臟鏈表中。
最后,回寫進程會將臟鏈表中的臟頁刷新到磁盤中,最終做到保證磁盤和內存中數據的一致性。
目前主流寫緩存策略就是回寫策略。
5.2 緩存回收
緩存回收,也叫緩存淘汰,作用有三個:
-
緩存中的數據清除(即清除頁的數據);
-
為更重要的緩存項提供空閑物理頁;
-
為了收縮緩存大小,以減輕內存的壓力。
緩存中的什么內容將被清除的策略或者什么樣的干凈頁可以回收的策略,就叫做緩存策略。
Linux的緩存回收是通過選擇干凈頁(非臟頁)進行簡單地替換——注意:干凈頁也是有數據的,只不過這個頁此時沒有回寫操作罷了。如果干凈頁不夠的話,內核強制進行回寫操作,以釋放出更多的干凈頁。
但是,哪些干凈頁是可以回收的呢?不能夠隨隨便便的就回收一個干凈頁啊。
對於回收策略Linux內核提供了兩種方法:
(1)最近最少使用
簡稱LRU。LRU回收策略需要跟蹤每個頁面的訪問蹤跡,以便回收最老時間戳的頁面。
該算法的效果在於:緩存的數據越久未被訪問,則越大可能不會再次被訪問,而近期訪問的,則很大可能會再次被訪問。
但是,LRU有一個致命的缺點——內核無法預測一個文件只會被訪問一次后不會再被訪問。
(2)雙鏈策略
這個是修改過后的LRU,內核維護的不再是一個LRU鏈表,而是兩個:活躍鏈表和非活躍鏈表。
處於活躍鏈表的頁面被認為是‘熱’的,且不會被換出(回收);而在非活躍鏈表上的也是會被回收的。
ps:寫到這里,你是否記得MySQL緩存淘汰機制?它倆是如此的相似!而且,不止是MySQL,還有Redis、文件系統、Ceph等等都可以見到LRU和改進版的身影!論理解內核的重要性:)
5.3 臟數據什么時候被寫到磁盤中?
臟數據:頁高速緩存的數據比后台存儲的數據更加新的時候,這些數據就叫做臟數據。
臟數據被回寫到磁盤的時機有3種:
-
當空閑內存低於一個特定閾值時。內核必須回寫數據以釋放內存,因為緩存回收只能回收干凈頁。當干凈頁數量足夠多時,內核就會收縮緩存,釋放內存。該值可以通過修改/proc/sys/vm/dirty_background_ratio設定(這個值是百分比,入文件中數字為10,則表示占全部內存的10%)
-
當臟頁在內存中存儲時間超過一個閾值后,內核必須將臟頁回寫入磁盤,確保臟頁不會無限期駐留內存。
-
用戶進程顯示調用sync()和fsync()系統調用時,內核會按要求將數據回寫進磁盤。
以上工作均有一群flusher線程執行。
為什么是一群了?想象下,如果是單個線程,當回寫操作很多時,就會造成擁塞。這是因為單一線程可能堵塞在某個設備的已擁塞請求隊列(正在等待將請求提交給磁盤的I/O請求隊列上),而其他設備的請求隊列無法得到處理。
在2.6.32內核中,flusher線程取代了pdflush線程,pdflush線程個數一般為2-8個;而pdflush又是取代最早的bdflush和kupdated兩個線程。
flusher線程的數目是根據磁盤數量變化的,一般每個磁盤對應一個線程。
5.4 頁面回寫可以人為設置嗎?
答案是可以的,上面講臟數據寫會磁盤時,就已經講了一個例子了,這里做一下匯總。
變量 | 功能 |
---|---|
/proc/sys/vm/dirty_background_ratio | 空閑內存閾值,占全部內存的百分比。內存中的空閑頁低於這個比例時,pdflush線程開始回寫臟頁 |
/proc/sys/vm/dirty_ratio | 空閑內存閾值,占全部內存的百分比。當一個進程產生的臟頁達到這個比例時,開始回寫 |
/proc/sys/vm/laptop_mode | 布爾值。用於控制膝上型計算機模式 |
6. 什么是膝上型計算機模式
這是一個特殊的回寫策略,該策略主要目標是:硬盤轉動的機械行為最小化,允許硬盤盡可能的長時間停滯,以此延長電池供電時間。
默認是關閉,可以設置開啟。一般較少使用。
但頁高速緩存和頁回寫是必須要搞清楚的,工作中很多情況都要考慮到這一點,比如寫計划任務時,如果計划任務中有操作同一個文件的,那這里就會有一個坑~~~
7.磁盤緩存和磁盤緩沖的區別和聯系是什么?緩存需要緩沖嗎?緩沖又需要緩存嗎?
區別:緩存(cache)是針對文件;而緩沖(buffer)是針對塊設備的。
頁高速緩存是緩存了緩沖的,這一部分就叫做緩沖區高速緩存,需要注意的是,緩沖區高速緩存是沒有作為獨立緩存的,而是作為頁高速緩存的一部分。
關於緩存和緩沖之間的聯系與區別,可以參考合在頁高速緩存里面的緩沖區高速緩存這篇文章。
最后我們捋一下磁盤、內存、文件系統與頁、塊之間的關系。
扇區:是塊設備中最小的可尋址單元(常見大小512字節);是塊設備的基本尋址和操作單元。
塊:是文件系統最小邏輯可尋址單元,文件系統的抽象,只能通過塊訪問文件系統。通常包含多個扇區。
頁:內核把物理頁作為內存管理的基本單元。塊必須小於等於頁,頁可以包含多個塊。
所以,三者的大小(或者說映射)關系是 頁 >= 塊 > 扇區。
我們今天的主題是頁高速緩存,page cache,我們從這些概念可以看出,它緩存的對象是文件系統,即磁盤上的數據。
而在上一章我們講述了一個概念:
緩沖區(buffer):磁盤塊在內存中的表示,是內核操作塊設備的邏輯單元。
所以,緩沖區也是可以緩存的,即緩沖區高速緩存,buffer cache。而現在的內核中它已經是頁高速緩存的一部分了。
后記
准備寫這篇文章有點猶豫,開始覺得這個主題較容易,可能相對內核其他知識還有MySQL原理,覺得不太值得一寫。
現在寫下來,終於明白了緩存與緩沖的區別。誰說不是一種收獲呢?以前也其實存在疑問,還在網上想找英文版(參考書),但只是看到了英文版的目錄。
想不到,在寫作的過程中,借鑒別人的文章時,終於搞明白了。多寫多看多交流!
當然還有一點,看書有疑問時,請看英文版!
參考資料:
《Linux內核設計與實現》原書第三版
https://blog.csdn.net/yjh314/article/details/78850571