摘要:Linux操作系統(tǒng)使用頁緩存機(jī)制來對塊設(shè)備上的文件進(jìn)行緩存,頁緩存可以無限增大,直到物理內(nèi)存達(dá)到內(nèi)存水線。在這種情況下,當(dāng)頁緩存使用過多的時候,內(nèi)核中一些原子性的內(nèi)存申請會失敗,導(dǎo)致一些業(yè)務(wù)執(zhí)行延時甚至失敗。文章通過對內(nèi)核中內(nèi)存管理模塊進(jìn)行改造,對頁緩存的申請進(jìn)行控制,使得頁緩存使用有最高限度。如果達(dá)到最高限度,新的頁緩存申請會替換掉頁緩存里舊的頁面。
關(guān)鍵詞:Linux操作系統(tǒng);頁緩存;主動回收
中圖分類號:TP31" 文獻(xiàn)標(biāo)志碼:A
0 引言
Linux內(nèi)核中有很多種類的內(nèi)存緩存,用于緩存各種需要頻繁從外設(shè)(如塊設(shè)備的磁盤等)進(jìn)行訪問的資源(如文件、inode等),以減少外設(shè)的訪問,提高系統(tǒng)響應(yīng)時間,本文涉及的頁緩存機(jī)制就是其中之一[1]。
很明顯這些緩存方式都是屬于以空間來換取時間的設(shè)計,那么就一定會在邏輯上存在一個問題,就是當(dāng)空間也在某種場景下成為一種瓶頸的時候,那么該如何平衡空間和時間。
本文從Linux的內(nèi)存機(jī)制尤其是緩存機(jī)制的分析開始,指出在何種場景下會出現(xiàn)頁緩存帶來的內(nèi)存不足的問題,提出一種基于主動回收的頁緩存限制方法來解決此類問題。
1 Linux內(nèi)核的內(nèi)存管理
1.1 頁框管理及回收機(jī)制
Linux的物理內(nèi)存按頁來進(jìn)行管理,每個頁都需要有一個頁描述符,類型為struct page,所有的頁描述符都存放在mem_map數(shù)組中。此數(shù)組在pg_data節(jié)點(diǎn)初始化時由初始內(nèi)存分配器alloc_bootmem_node接口分配占用空間[2]。
在不考慮非一致內(nèi)存的情況下,整個內(nèi)存情況被描述成一個內(nèi)存節(jié)點(diǎn)pg_data,這個內(nèi)存節(jié)點(diǎn)被分為3個管理區(qū):DMA區(qū)、NORMAL區(qū)、HIGHMEM區(qū)。每個區(qū)管理本區(qū)地址范圍內(nèi)的所有頁面,尤其是空閑頁面,空閑頁面按照伙伴算法進(jìn)行分配和釋放。每個內(nèi)存區(qū)有3個水平值:pages_high、pages_low、pages_min。用這3個閾值來判定目前空閑頁面的水平,從而決定需要做何動作。內(nèi)存頁面回收時機(jī)如圖1所示。
圖1說明了在申請內(nèi)存時,空閑內(nèi)存到達(dá)哪個水平時,需要做的動作。當(dāng)空閑頁面小于page_low時,喚醒回收線程kswapd;當(dāng)小于page_min時,同步調(diào)用回收過程try_to_free_pages;當(dāng)空閑頁面回到page_low水平時,kswapd線程再度睡眠。
1.2 Linux的磁盤緩存機(jī)制
磁盤上的數(shù)據(jù)需要被用到時,都需要先讀到內(nèi)存里,這就是磁盤緩存的作用。Linux下的磁盤緩存有:目錄項(xiàng)高速緩存、索引節(jié)點(diǎn)inode高速緩存,塊緩存和頁緩存[3]。前兩者比較專用且數(shù)據(jù)量也并不大,因此本節(jié)只討論后兩者。
塊設(shè)備是指最小存儲訪問粒度為塊的設(shè)備。一般最小的IO和存儲單位為塊,比如硬盤扇區(qū),一般為512字節(jié),因此塊設(shè)備的緩存也是以塊為單位的,這就是塊緩存,早期的Linux內(nèi)核和Unix內(nèi)核都只有塊緩存。
但現(xiàn)代的塊設(shè)備一般可以支持直接按頁面(4k)的IO粒度,比如:在塊設(shè)備IO請求結(jié)構(gòu)BIO中,是直接可以指定一個頁面的,而且內(nèi)核下物理內(nèi)存的管理也是以頁面為粒度[4]。這樣,對塊設(shè)備的緩存不如以頁為單位。以頁為單位的緩存就是頁緩存。目前,Linux內(nèi)核主要使用頁緩存機(jī)制,但塊緩存機(jī)制仍然需要保留,理由如下。
(1)在磁盤空間比較緊張的情況下,碎片也比較多,有些文件可能不能連續(xù)存儲在相鄰的塊里,這樣會使得頁緩存粒度太大,必須使用塊緩存,因?yàn)閴K設(shè)備的最小存儲單位是塊。
(2)文件系統(tǒng)的super block超級塊和inode塊,只能按塊來訪問。另外,對于不利用文件系統(tǒng)而直接訪問設(shè)備塊的軟件,也要提供接口。
塊緩存雖然比頁小,但由于物理內(nèi)存以頁為單位管理,因此塊緩存也放在頁里,這種頁叫做緩存區(qū)頁;塊緩存因?yàn)橐詨K為單位,因此不需要跟文件關(guān)聯(lián),需要跟塊設(shè)備關(guān)聯(lián),如圖2所示。
一個緩沖區(qū)頁的private字段指向頁內(nèi)塊緩存的頭,本頁所有塊緩存的頭是一個單鏈表,每個塊緩存頭的b_page字段指向緩存區(qū)頁,塊緩存頭的b_bdev字段指向塊設(shè)備描述符,這樣可以完整地把塊緩存組織起來。
頁緩存和塊緩存所占用物理頁框也都被鏈接入內(nèi)核active和inactive隊列,以用于內(nèi)存回收。
磁盤上需要有磁盤文件,系統(tǒng)才能進(jìn)行文件的存放和讀取。因此,在討論塊設(shè)備的緩存時,需要考慮不同文件系統(tǒng)的實(shí)現(xiàn)。
嵌入式系統(tǒng)下可執(zhí)行文件一般直接放入Linux的根文件系統(tǒng)中,Linux的根文件系統(tǒng)用到了如下3種文件系統(tǒng):Ramfs、Jffs2和Squashfs。Ramfs是基于內(nèi)存的文件系統(tǒng),本項(xiàng)目一般使用initrd形式的根文件系統(tǒng)。后兩者則都是基于Flash的文件系統(tǒng),Linux直接加載Flash上已經(jīng)被燒結(jié)好的Jffs2/Squashfs文件系統(tǒng)分區(qū)作為根文件系統(tǒng)。
Ramfs:Ramfs的存儲介質(zhì)就是內(nèi)存,所有文件系統(tǒng)的數(shù)據(jù)都直接存放在頁緩存里面,或者換句話說,頁緩存就是Ramfs的最終存放介質(zhì)。因此,Ramfs系統(tǒng)只用到了頁緩存。
Squashfs:只讀并且壓縮的文件系統(tǒng),即存放在Flash上的數(shù)據(jù)是經(jīng)過壓縮的,在讀到內(nèi)存中被系統(tǒng)使用之前要經(jīng)過解壓縮,同樣被寫進(jìn)去之前也要經(jīng)過壓縮。Squashfs把Flash上的未解壓的裸數(shù)據(jù)先讀到塊緩存中,然后解壓到頁緩存中被系統(tǒng)使用,寫時反之。因此,Squashfs既使用了頁緩存,也使用了塊緩存。
Jffs2:常用于Flash上的日志型文件系統(tǒng),也可以壓縮,但其解壓和壓縮過程直接使用Kmalloc申請內(nèi)存進(jìn)行,不使用塊緩存。因此,Jffs2文件系統(tǒng)只使用了頁緩存。
1.3 頁緩存管理
頁緩存機(jī)制是Linux內(nèi)核對塊設(shè)備上存儲文件的一種內(nèi)存緩存機(jī)制,在Linux內(nèi)核需要讀取塊設(shè)備上的文件時,先將文件內(nèi)容緩存在頁緩存的頁面中,后續(xù)再使用該文件時則不需要從塊設(shè)備上重新讀取,從而節(jié)省時間[5]。
圖3列出了一個用戶態(tài)進(jìn)程的2個線性區(qū)(分別為代碼段和數(shù)據(jù)段)共同映射了一個文件的情況。2個線性區(qū)數(shù)據(jù)結(jié)構(gòu)vm_area共同指向一個文件數(shù)據(jù)結(jié)構(gòu)file,file結(jié)構(gòu)的address_space結(jié)構(gòu)里含有所有本文件已經(jīng)被讀入內(nèi)存的頁框數(shù)據(jù)結(jié)構(gòu)page,只要把vm_area線性區(qū)中的線性地址與物理頁框的映射關(guān)系加入進(jìn)程頁表,就可以直接訪問了。其中,文件的數(shù)據(jù),即文件的代碼和全局?jǐn)?shù)據(jù),存放在頁緩存的物理頁框中[6]。
頁緩存是文件的緩存,需要與某個文件相關(guān),這是依靠一個address_space對象來實(shí)現(xiàn),而且一個文件一般也會有很多頁緩存存在,一個文件所擁有的所有頁緩存是通過一個基數(shù)樹來管理的,如圖4所示。
圖4是Linux的頁緩存機(jī)制基本原理,Linux內(nèi)核設(shè)計頁緩存可以使用所有剩余物理內(nèi)存空間,如果內(nèi)存不足時,依靠回收機(jī)制進(jìn)行回收,一般有2種回收時機(jī)。(1)在內(nèi)存申請的時候,如果空閑內(nèi)存低于低水線(page_low)時,則喚醒回收線程kswapd進(jìn)行異步回收。(2)在內(nèi)存申請的時候,如果空閑內(nèi)存低于最小水線(page_min)時,申請內(nèi)存的上下文可以等待的話,則進(jìn)行同步調(diào)用和同步回收。
因此,可以看到,Linux對于頁緩存的設(shè)計是非常靈活高效的。但同時它也存在很大的問題,頁緩存可以無限增大,直到物理內(nèi)存達(dá)到內(nèi)存水線,此時內(nèi)核中一些原子性的內(nèi)存申請(因?yàn)槠洳荒苤苯舆M(jìn)行內(nèi)存回收)就會失敗,導(dǎo)致一些業(yè)務(wù)執(zhí)行延時甚至失敗。這在一些對可靠性要求較高的網(wǎng)絡(luò)通信設(shè)備中可能是無法接受的。
2 Linux頁緩存機(jī)制的問題
2.1 頁緩存機(jī)制帶來的問題
Linux的頁緩存機(jī)制可以使用所有剩余物理內(nèi)存,以提高文件訪問效率,但也會有一些不合適的場景,尤其是在一些嵌入式的Linux環(huán)境中。
例如:在一些家庭網(wǎng)關(guān)或家用路由器應(yīng)用中,開啟了UPNP媒體業(yè)務(wù)的情況下,媒體掃描業(yè)務(wù)需要讀取磁盤上的大量媒體文件,從而占用大量的頁緩存空間,最終將物理內(nèi)存占滿。在內(nèi)存水線配置較低的情況下,內(nèi)核中的原子性(不能睡眠)內(nèi)存分配因?yàn)閮?nèi)存不足而失敗,如下為內(nèi)存失敗時的內(nèi)核日志:
sirq-tasklet/0: page allocation failure. order:0, mode:0x20
Call Trace:[lt;80008aacgt;]…
Mem-info:
DMA per-cpu:
CPU 0: Hot: hi: 0, btch: 1 usd: 0 Cold: hi: 0, btch: 1 usd: 0
Normal per-cpu:
CPU 0: Hot: hi: 0, btch: 1 usd: 0 Cold: hi: 0, btch: 1 usd: 0
Active:1698 inactive:2649 dirty:0 writeback:0 unstable:0
free:33 slab:1417 mapped:233 pagetables:113 bounce:0
DMA free:96kB min:88kB low:720kB high:808kB active:2588kB inactive:4036kB present:16256kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 15
Normal free:36kB min:88kB low:720kB high:808kB active:4204kB inactive:6560kB present:16256kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0
DMA: 0*4kB 0*8kB 0*16kB 1*32kB 1*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 96kB
Normal: 1*4kB 0*8kB 0*16kB 1*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 36kB
Free swap: 0kB
8192 pages of RAM
0 pages of HIGHMEM
1055 reserved pages
1893 pages shared
0 pages swap cached
如上,出現(xiàn)內(nèi)存不足時,內(nèi)存幾乎被耗盡,原因是頁緩存占用內(nèi)存較多(Active:1698 inactive:2649),內(nèi)存水線設(shè)置也比較接近,回收不及時,導(dǎo)致內(nèi)核中原子性的(mode:0x20)內(nèi)存申請失敗。
2.2 傳統(tǒng)規(guī)避方法和局限性
要解決上述問題,在原生的Linux系統(tǒng)條件下,有以下2種方法。方法一:內(nèi)核文件/proc/sys/vm/drop_caches中寫入1,可以觸發(fā)內(nèi)核主動進(jìn)行page cache的回收;方法二:提高內(nèi)存水線設(shè)置,更加提前觸發(fā)內(nèi)核進(jìn)行內(nèi)存回收。方法一可以回收所有不用的頁緩存,但也存在2個問題,(1)回收的時機(jī)無法準(zhǔn)確確定;(2)釋放全部過期緩存,對性能也有影響。方法二是被動方法,加快回收,但水線設(shè)置值很難抉擇,而且影響全局,設(shè)置不好也會導(dǎo)致系統(tǒng)忙于回收等效率問題。可見,以上2種現(xiàn)有辦法都無法完美解決上述問題。
3 基于主動回收的Linux頁緩存限制方法
3.1 頁緩存限制方法和主動回收
如果對頁緩存有主動限制辦法,比如限制頁緩存最多能使用多少內(nèi)存,問題就比較容易解決了。為此設(shè)計如下改造方法,其原理如下:增加一個內(nèi)核sysfs文件/proc/sys/vm/pagecache_ratio,此文件設(shè)置一個百分比,含義為最多頁緩存可以占用物理內(nèi)存的比率;內(nèi)存管理系統(tǒng)初始化的時候,根據(jù)上述比率計算每個區(qū)域能使用的最多頁緩存頁數(shù);增加一種內(nèi)存頁面申請標(biāo)志GFP_PAGECACHE,只要是申請頁緩存頁面的動作都標(biāo)注此標(biāo)志,但要除去比如ramfs,tmpfs等必須占用頁緩存的內(nèi)存文件系統(tǒng)申請;在頁面申請操作中,如果是GFP_PAGECACHE類型的申請,判斷頁緩存是否超過預(yù)定限制。如果超過,則不能申請到,觸發(fā)內(nèi)存回收;內(nèi)存回收時,如果發(fā)現(xiàn)只是頁緩存超限,則只回收頁緩存頁面,不回收匿名頁面;如上,限制的基本原理就是利用現(xiàn)有的內(nèi)存回收機(jī)制,單獨(dú)提前為頁緩存進(jìn)行控制。
另外,在各使用場景中,需要根據(jù)自身情況多加考慮。標(biāo)注申請頁緩存頁面的動作一般是由各文件系統(tǒng)決定的,因此,需要考量所在系統(tǒng)都使用了哪些文件系統(tǒng),這些文件系統(tǒng)的頁緩存頁面操作是使用的什么接口,保證在申請時加入GFP_PAGECACHE標(biāo)志。
3.2 試驗(yàn)結(jié)果分析
修改Linux內(nèi)核源碼,加入上述的頁緩存限制功能后,在一個小內(nèi)存(32 M)的家用路由器設(shè)備上進(jìn)行試驗(yàn)。
(1)對頁緩存限制進(jìn)行配置。
# cat /proc/sys/vm/pagecache_ratio
32
如上,表示配置頁緩存最多能使用物理內(nèi)存的32%;
計算后系統(tǒng)內(nèi)存的數(shù)量和頁緩存的限制如下。
pages" present 8128
max pagecache pages: 2600
pagecaches alloc failed: 48
總內(nèi)存大小為8128頁面,頁緩存最大限制為2600/8128=32%,符合上面的設(shè)置。
(2)上電后查看free內(nèi)存和頁緩存使用內(nèi)存。
Free內(nèi)存:
MemFree:" 2860 kB
nr_file_pages 2275
nr_file_ramfs 0
nr_file_tmpfs 0
pagecaches alloc failed: 48
2275個頁面(須減去ramfs和tmpfs的占用數(shù))小于2600的限制,頁緩存申請失敗次數(shù)不為0,說明限制生效。
如上,通過對頁緩存的限制,限制了系統(tǒng)緩存的最大峰值,保證了剩余內(nèi)存的數(shù)量,極大地減少了內(nèi)核中緊急業(yè)務(wù)原子性申請內(nèi)存失敗的情況。
在上述家用路由器設(shè)備的實(shí)際測試過程中,未限制之前,開啟媒體解析和播放服務(wù)后,啟動數(shù)據(jù)業(yè)務(wù)必然出現(xiàn)原子內(nèi)存申請失敗告警現(xiàn)象,導(dǎo)致媒體服務(wù)無法交付使用。使用頁緩存限制后,同樣開啟媒體服務(wù),啟動內(nèi)核數(shù)據(jù)業(yè)務(wù),大負(fù)荷運(yùn)行若干天,不再出現(xiàn)原子內(nèi)存分配失敗告警問題。
4 結(jié)語
綜上所述,利用本文提出的頁緩存限制方法后,對空閑內(nèi)存缺少導(dǎo)致的原子內(nèi)存申請失敗問題有根本性的改善。根據(jù)內(nèi)存壓力情況和文件讀取效率進(jìn)行限制比率調(diào)整:如果內(nèi)存壓力較大,可以調(diào)低頁緩存限制比率,這樣頁緩存占用內(nèi)存會降低;如果文件讀取效率降低,但是內(nèi)存壓力較小,可以調(diào)高頁緩存限制比率,這樣頁面替換速度會降低,提高文件讀取命中效率。
在嵌入式設(shè)備中,內(nèi)存資源一般都是有限的。在這種情況下,出現(xiàn)內(nèi)存壓力時,在顯式的內(nèi)存裁剪不再奏效時,應(yīng)該首先考慮對系統(tǒng)緩存進(jìn)行限制,以可以接受的效率降低來換取更大的空間。
參考文獻(xiàn)
[1]汪敏.Linux中多種內(nèi)存共享機(jī)制及其應(yīng)用探究[J].無線互聯(lián)科技,2023(4):1-4,22.
[2]吳懿.基于ARM的嵌入式Linux的內(nèi)存優(yōu)化技術(shù)研究與實(shí)現(xiàn)[D].南京:南京航空航天大學(xué),2011.
[3]趙興華.開啟Linux新內(nèi)核特性實(shí)現(xiàn)內(nèi)存管理優(yōu)化[J].網(wǎng)絡(luò)安全和信息化,2024(7):163-165.
[4]楊淵,鄒祖?zhèn)?軟硬協(xié)同的嵌入式系統(tǒng)存儲可靠性增強(qiáng)設(shè)計[J].太赫茲科學(xué)與電子信息學(xué)報,2024(2):219-226.
[5]俞丁翠,羅龍飛,宋云鵬,等.面向高密度閃存的內(nèi)存頁大小探索[J].計算機(jī)工程與科學(xué),2024(7):1167-1174.
[6]郭鋒,王宏偉,黃保壘,等.嵌入式操作系統(tǒng)中基于MIPS處理器的內(nèi)存管理機(jī)制實(shí)現(xiàn)[J].無線互聯(lián)科技,2020(11):109-110,118.
(編輯 王永超)
Restriction method of Linux page cache based on active reclaiming
WANG" Lei, ZHANG" Bo, XIE" Tiemin
(Nanjing Branch of Triples (Shenzhen) Communication Technology Co., Ltd., Nanjing 211100, China)
Abstract: The Linux operating system uses a page cache mechanism to cache files on block devices, and the page cache can be infinitely increased until the physical memory reaches the memory watermark. In this case, when the page cache is used excessively, some atomic memory requests in the kernel may fail, resulting in delayed or even failed. The article modifies the memory management module in the kernel to control the use of page cache, so as to maximize the use of page cache. If the maximum limit is reached, the new page cache application will replace the old pages in the page cache.
Key words: Linux operation system; page cache; active reclaiming