Linux的Page Cache
Page Cache
何為Page Cache
為了了解Page Cache我們可以看一下Linux的文件I/O系統(tǒng)

從圖中可以看出,Page Cache是由Linux內(nèi)核進(jìn)行管理的,而且通過mmap以及bffered I/O將文件讀取到內(nèi)存空間實(shí)際上都是讀取到Page Cache上的。
如何查看系統(tǒng)的Page Cache?
通過讀取cat /proc/meminfo
文件,查看系統(tǒng)實(shí)時內(nèi)存情況。
?...
?Buffers: ? ? ? ? ?117572 kB
?Cached: ? ? ? ? ?2738632 kB
?SwapCached: ? ? ? ?14560 kB
?Active: ? ? ? ? ?1444220 kB
?Inactive: ? ? ? ?5806180 kB
?Active(anon): ? ? 509816 kB
?Inactive(anon): ?4946608 kB
?Active(file): ? ? 934404 kB
?Inactive(file): ? 859572 kB
?...
?Shmem: ? ? ? ? ? 1062864 kB
?...
?Buffers + Cached + SwapCached = Active(file) + Inactive(file) + Shmem + SwapCached
上述等式兩邊都是Page Cache
?Page Cache = Buffers + Cached + SwapCached
Page 于Page Cache
Page 是內(nèi)存管理分配的基本單位,Page Cache是由多個Page所構(gòu)成的。Page在操作系統(tǒng)中通常都是為4KB大小,Page Cache的大小為4KB的整數(shù)倍。
另外一方面,并不是所有的Page都被認(rèn)為是Page Cache
Linux系統(tǒng)中可供訪問的兩種內(nèi)存:
File-backed pages:文件備份頁也就是Page Cache中的Page,對應(yīng)于磁盤上的若干數(shù)據(jù)塊;對于這些頁最大的問題是臟數(shù)據(jù)回盤;
Anonymous pages: 不對應(yīng)磁盤上的任何數(shù)據(jù)塊,也就是晉城運(yùn)行時的內(nèi)存空間(例如:方法棧、局部變量表等屬性)
為啥不直接將Page Cache稱為block cache
從磁盤中加載的數(shù)據(jù),不僅僅放在Page Cache中,還會放在Bufer cache。
例如:通過Direct I/O技術(shù)的磁盤文件就可以不進(jìn)入Page Cache中。
歷史設(shè)計原因,隨著linux的演進(jìn)也逐漸不同
對比一下file-backed pages和Anonymous pages在swap機(jī)制下的性能
內(nèi)存是一種珍惜資源,當(dāng)內(nèi)存不夠用的時候,內(nèi)存管理單元MMU(memory Managment Unit)需要提供調(diào)度算法回收相關(guān)的內(nèi)存空間。內(nèi)存空間揮手的方式通常就是swap機(jī)制,將內(nèi)存中的數(shù)據(jù)交換到持久化設(shè)設(shè)備上的過程。
File-backed pages(Page Cache)的回收代價比較低,因為:
Page Cache通常對應(yīng)于一個文件上的若干順序塊,因此我們在進(jìn)行持久化落盤可以通過順序I/O的方式進(jìn)行落盤。
如果Page Cache上沒有進(jìn)行任何的寫操作(也就是沒有任何的臟頁),甚至Linux系統(tǒng)不會將Page Cache進(jìn)行回盤,想要獲取新的內(nèi)容完全可以通過再次讀取磁盤中的文件即可。
Page Cahe主要難點(diǎn)在于臟頁回盤(后面進(jìn)行說明)
Anonymous pages內(nèi)存回收代價是比較高的。這時因為Anonymous pages通常隨機(jī)地寫入持久化設(shè)備。另外一方面,無論是否有寫操作,為了確保數(shù)據(jù)不丟失,Anonymous pages在進(jìn)行swap時必須持久化到磁盤上。
swap與缺頁中斷
swap機(jī)制是指物理內(nèi)存不夠用的時候,內(nèi)存管理單元MMU(memory Managment Unit)需要提供調(diào)度算法來回收相關(guān)的內(nèi)存空間,然后將清理出來的內(nèi)存空間給當(dāng)前的內(nèi)存申請方。
swap機(jī)制存在的原因是Linux系統(tǒng)提供了虛擬內(nèi)存管理機(jī)制,每個一個進(jìn)程都認(rèn)為其獨(dú)占內(nèi)存空間,因此所有的進(jìn)程的內(nèi)存空間之和遠(yuǎn)遠(yuǎn)大于物理內(nèi)存,所有進(jìn)程的內(nèi)存空間之和超過物理內(nèi)存的分部分需要交換到磁盤上。
操作系統(tǒng)以page為單位管理內(nèi)存,當(dāng)進(jìn)程發(fā)現(xiàn)需要訪問的數(shù)據(jù)不再內(nèi)存時,操作系統(tǒng)可能會將數(shù)據(jù)以頁的方式加載到內(nèi)存中。上述的整個過程被稱之為缺頁中斷,當(dāng)操作系統(tǒng)發(fā)生缺頁中斷時,就會通過系統(tǒng)調(diào)用將page再次讀取到內(nèi)存中。
但主內(nèi)存的空間是有限的,當(dāng)主內(nèi)存中不包含可以使用的空間時,操作系統(tǒng)會從內(nèi)存中選擇合適的內(nèi)存頁驅(qū)逐回磁盤,為新的內(nèi)存頁讓出位置,選擇待驅(qū)逐頁的過程在操作系統(tǒng)中叫做頁面替換(page replacement),替換操作又會觸發(fā)swap機(jī)制。
如果物理內(nèi)存足夠大,那么可能不需要 Swap 機(jī)制,但是 Swap 在這種情況下還是有一定優(yōu)勢:對于有發(fā)生內(nèi)存泄漏幾率的應(yīng)用程序(進(jìn)程),Swap 交換分區(qū)更是重要,這可以確保內(nèi)存泄露不至于導(dǎo)致物理內(nèi)存不夠用,最終導(dǎo)致系統(tǒng)崩潰。但內(nèi)存泄露會引起頻繁的 swap,此時非常影響操作系統(tǒng)的性能。
Linux可以通過swappiness參數(shù)控制swap機(jī)制,范圍[0-100],實(shí)現(xiàn)控制swap的優(yōu)先級:
高數(shù)值:較高頻率的swap,在進(jìn)程不活躍時主動將其轉(zhuǎn)換出物理內(nèi)存
低數(shù)值:較低頻率的swap,這可以確保交互式不因為內(nèi)存空間頻繁的=地交換到磁盤而提高響應(yīng)延遲。
為什么buffers也是Page Cache的一部分
這是因為當(dāng)匿名頁(Inactive(anon) 以及 Active(anon))先被交換(swap out)到磁盤上后,然后再加載回(swap in)內(nèi)存中,由于讀入到內(nèi)存后原來的 Swap File 還在,所以 SwapCached 也可以認(rèn)為是 File-backed page,即屬于 Page Cache。這個過程如 Figure 2 所示。

老一點(diǎn)的系統(tǒng)
?~ free -m
? ? ? ? ? ? ? total ? ? ? used ? ? ? free ? ? shared ? ?buffers ? ? cached
?Mem: ? ? ? ?128956 ? ? ?96440 ? ? ?32515 ? ? ? ? ?0 ? ? ? 5368 ? ? ?39900
?-/+ buffers/cache: ? ? ?51172 ? ? ?77784
?Swap: ? ? ? ?16002 ? ? ? ? ?0 ? ? ?16001
Cached 表示當(dāng)前的頁緩存(Page Cache)占用量,buffers表示的是當(dāng)前塊緩存(buffer Cache)占用量。
Page Cache : 用于緩存文件的頁數(shù)據(jù)
buffer Cache: 用于緩存塊設(shè)備如磁盤的塊數(shù)據(jù)
新系統(tǒng)
?andrew@andrew-G3-3590:~$ free -m
? ? ? ? ? ? ? ?總計 ? ? ? ? 已用 ? ? ? ?空閑 ? ? ?共享 ? ? 緩沖/緩存 ? ? ? ?可用
?內(nèi)存: ? ? ? ?7789 ? ? ? ?3190 ? ? ? ? 627 ? ? ?1111 ? ? ? 3970 ? ? ? ?3212
?交換: ? ? ? ?2047 ? ? ? ? ? 1 ? ? ? ?2046
通過上述描述,我們可以 知道Page Cache與文件系統(tǒng)同級別的,buffer Cache是塊物理上的概念,因此buffer Cache是與塊設(shè)備驅(qū)動程序是同級別的。
Page Cache與buffer Cache共同的目的就是為了加速數(shù)據(jù)I/O: 寫數(shù)據(jù)時首先要寫到緩存,將寫入的數(shù)據(jù)標(biāo)記為dirty,想外部存儲flash(簡稱回盤)。如果讀取數(shù)據(jù)我們會先去緩存中讀取,如果在緩存中未命中,我們再去外部存儲中去讀取,并將讀取出來的數(shù)據(jù)加入到緩存中,并且操作系統(tǒng)總是積極的將所有的空閑內(nèi)存都用作Page Cache和buffer Cache,當(dāng)內(nèi)存不夠用時也會使用LRU等算法淘汰緩存。
Linux2.4之前,Page Cache與buffer Cache是完全分離的,但是塊設(shè)備通常是磁盤,磁盤上的數(shù)據(jù)又大多通過文件系統(tǒng)來組織,這種設(shè)計方式會導(dǎo)致很多數(shù)據(jù)被緩存了兩次。在LInux系統(tǒng)2.5版本之后,系統(tǒng)將兩個快近似的融合在了一起,如果一個文件的頁加載到了Page Cache,那么同時Buffer Cache只需要維護(hù)塊指向頁的指針就可以了。只有那些沒有文件表示的塊,或者繞過文件系統(tǒng)直接操作(dd命令)的塊,才會真正的放到Buffer Cache里。后文所說的Page Cache基本上是Page Cache與buffer Cache的統(tǒng)稱。

Page Cache的預(yù)度
操作系統(tǒng)會為基于Page Cache的讀緩存機(jī)制提供預(yù)讀機(jī)制(PAGE_READAHEAD)
舉個例子:用戶想通過系統(tǒng)調(diào)用獲取磁盤上文件的一段數(shù)據(jù)
用戶線程僅僅想讀取磁盤上對應(yīng)文件的0-3KB范圍內(nèi)的數(shù)據(jù),由于磁盤的基本讀寫單位是block(4KB),于是操作系統(tǒng)會至少讀取4KB的內(nèi)容,這4KB剛好能夠在一個Page中裝下。
由于操作系統(tǒng)出于局部最優(yōu)原理,會將磁盤塊offset[4,8],[9,12]以及[1316]都加載到內(nèi)存
經(jīng)過上述操作,我們只是想通過read讀取4KB的內(nèi)容,但是系統(tǒng)預(yù)讀取了16KB的內(nèi)容。

Page Cache與文件持久化的一致性&可靠性
現(xiàn)在LInux的Page Cache正如其名,是對磁盤上Page的內(nèi)存緩存,同時可以用于讀寫操作。任何系統(tǒng)引入緩存,就會引發(fā)一致性問題:內(nèi)存中的數(shù)據(jù)與磁盤中的數(shù)據(jù)不一致,例如后端常見的Redis緩存和MySQL數(shù)據(jù)庫的一致性問題
什么是文件
文件其實(shí)是數(shù)據(jù)和元數(shù)據(jù)的組合,文件=數(shù)據(jù)+元數(shù)據(jù)。
元數(shù)據(jù):用來描述文件的各種屬性,元數(shù)據(jù)也必須存儲在磁盤上
因此,我們在保證數(shù)據(jù)一致性的時候其實(shí)包含了兩個方面:數(shù)據(jù)一致+元數(shù)據(jù)一致
?# drwxr-xr-x ?5 andrew andrew ?4096 4月 ?24 16:43 snap
?# 元數(shù)據(jù)包含的內(nèi)容
?# 1. 文件的大小
?# 2. 創(chuàng)建時間
?# 3. 訪問的時間
?# 4. 屬主和屬組等信息
?# 5. 屬主權(quán)限信息
文件一致性
發(fā)生寫操作的時候,我們會把對應(yīng)的數(shù)據(jù)寫到Page Cache中,如果此時數(shù)據(jù)還沒有刷新到磁盤中,那么內(nèi)存中的數(shù)據(jù)就領(lǐng)先于磁盤,此時Page就被稱為Dirty page。
當(dāng)前的lInux有兩種方式實(shí)現(xiàn)文件一致性:
寫穿(write through): 內(nèi)核向用戶提供一種接口,通過該接口操作文件可以保證文件一致性
寫回(write back): 系統(tǒng)提供定時任務(wù),該任務(wù)周期性同步文件系統(tǒng)臟數(shù)據(jù),這時Linux的默認(rèn)同步方案。
方法含義fsync(int fd)將fd代表的文件的臟數(shù)據(jù)和臟元數(shù)據(jù)刷新到磁盤中fdatasync(int fd)將fd代表的文件的臟數(shù)據(jù)刷新到磁盤,同時對必要的元數(shù)據(jù)刷新到磁盤,必要信息是指對文件有關(guān)鍵作用的信息。文件大小、文件修改時間就不屬于必要信息了。sync()對系統(tǒng)中的所有臟數(shù)據(jù)和臟元數(shù)據(jù)刷新到磁盤中
Page Cache的優(yōu)勢與劣勢
優(yōu)勢
加快對數(shù)據(jù)的訪問
減少磁盤I/O的訪問次數(shù),提高系統(tǒng)磁盤壽命
減少對磁盤I/O的訪問,提高系統(tǒng)磁盤I/O吞吐量(Page Cache的預(yù)讀機(jī)制)
劣勢
使用額外的物理內(nèi)存空間,當(dāng)物理內(nèi)存比較緊俏的時候,可能會導(dǎo)致頻繁的swap操作,最終會導(dǎo)致系統(tǒng)的磁盤I/O負(fù)載上升。
Page Cache沒有給應(yīng)用層提供一個很好的API。導(dǎo)致應(yīng)用層想要優(yōu)化Page Cache的使用策略很難。因此一些應(yīng)用實(shí)現(xiàn)了自己的Page管理,比如MySQL的InnoDB存儲引擎以16KB的頁進(jìn)行管理。
在某些應(yīng)用場景下,比如我們每次打開文件只需要讀取或者寫入幾個字節(jié)的情況,會比Direct I/O多一些磁盤的讀取于寫入。
參考:
《Linux內(nèi)核詳解》
博客地址:
https://wangyazhou.blog.csdn.net/article/details/120240742
視頻地址:
https://www.bilibili.com/video/BV1YP4y1Y7dS?share_source=copy_web
https://www.bilibili.com/video/BV1kg411F7ZP?spm_id_from=333.999.0.0
https://www.bilibili.com/video/BV1BM4y1G7qP?spm_id_from=333.999.0.0
https://www.bilibili.com/video/bv1CA411F7vW