救命,Linux正在吃掉我的內(nèi)存!
內(nèi)存發(fā)現(xiàn)自己的空閑空間越來越少,經(jīng)過一番調(diào)查,發(fā)現(xiàn)罪魁禍?zhǔn)拙尤皇荓inux老大!
內(nèi)存:Linux老大,這也沒幾個程序在運(yùn)行,可是你為什么老是占用我的內(nèi)存啊,內(nèi)存都快被你吃光了!
Linux :你的容量那么大,空間閑著也是閑著,我啊,把那些空閑的空間都當(dāng)成文件緩存了!

內(nèi)存: 你看看你, 把這文件“拆成”了4K的碎片,這兒放一片,那兒放一片,把我的內(nèi)存空間搞得亂糟糟的。
Linux :這叫做Page Cache , 其實(shí)一點(diǎn)也不亂,文件在哪一“片”內(nèi)存中放著,我記得清清楚楚。我這么做也是不得已而為之啊,硬盤太慢,比你慢幾萬倍。CPU的一秒,你這里就是6分鐘,硬盤那里就是好幾個月!每次從他那里讀點(diǎn)兒數(shù)據(jù),幾個月才給我回話, 我只好把讀出來的數(shù)據(jù)先緩存到你這里了。

內(nèi)存看到這張表格,不由得咂舌:沒想到這外面的世界如此之慢啊!
正在此時, 一個叫helloworld的程序要讀取文件。
helloworld :老大,我給你發(fā)了一個read系統(tǒng)調(diào)用,要讀取config.txt的前1024個字節(jié),把結(jié)果放到我的buffer中。
Linux :好,讓我看看config.txt是不是已經(jīng)在Page Cache中了,真不巧,還沒緩存過。內(nèi)存老弟,我又要吃你的空閑空間了。
Linux在內(nèi)存中分配了一個4k 大小的page frame, 向硬盤發(fā)出DMA指令,讀取cong.txt的4k的數(shù)據(jù)。

內(nèi)存感覺奇怪:人家helloworld只要1024個字節(jié),你干嘛讓硬盤發(fā)過來4K數(shù)據(jù)?
Linux :我這里Page cache 都是以4K為單位的, 讀一次得等幾個月,還不多讀一點(diǎn)?再說helloworld這小子很可能繼續(xù)讀文件的后續(xù)部分,下次就不用訪問硬盤了。
過了“幾個月” , 硬盤的數(shù)據(jù)復(fù)制到了內(nèi)存的Page cache 中

內(nèi)存說:這就完事了吧?
Linux :怎么可能!我得從Page cache 中取出前1024個字節(jié),復(fù)制到helloworld指定的buffer 中。
這個buffer其實(shí)是helloworld虛擬地址空間heap上的地址,物理地址也是在你的內(nèi)存中。

內(nèi)存:我的天!難道數(shù)據(jù)要在我內(nèi)存中出現(xiàn)兩份?
Linux :沒錯!你不知道,復(fù)制數(shù)據(jù)還得用CPU呢!很費(fèi)勁的。
helloworld:老大,能不能讓我直接訪問你Page cache 中的數(shù)據(jù)?
Linux :那怎么行,你在用戶空間,我在內(nèi)核空間,你要是能訪問,在我這里搗亂怎么辦?必須禁止!
內(nèi)存:嗯,有道理,不過,要是還有個程序,也要讀取config.txt的前1024個字節(jié),怎么辦?
Linux: 那就簡單了啊, 我一查就知道數(shù)據(jù)已經(jīng)在Page cache中了,不用等幾個月從硬盤讀了,直接復(fù)制到那個程序的緩沖區(qū)就行了。
內(nèi)存:啊?這數(shù)據(jù)重復(fù)太多了吧!

Linux :嗯,確實(shí)是個問題,現(xiàn)在這些程序,動輒訪問幾十個文件,每個程序都復(fù)制一份,確實(shí)是巨大的浪費(fèi)。
內(nèi)存:我給你支個招,既然那些程序運(yùn)行訪問的都是虛擬地址,你讓這些虛擬地址映射到Page cache上,大家不就可以共享了。
Linux :好主意,我來提供一個叫做mmap的系統(tǒng)調(diào)用,完成你說的功能。

helloworld運(yùn)行結(jié)束,退出了。
內(nèi)存: helloworld退出了,你一會兒會清理掉對應(yīng)的page cache吧?
Linux:暫時不會!
內(nèi)存:啊?怪不得內(nèi)存快被你吃光了!
Linux : 唉呀,你的內(nèi)存閑著也是閑著,文件緩存著,下次再訪問的時候,性能會有巨大提升!你放心,我會在合適的時機(jī)清理掉page cache的。
內(nèi)存:那如果helloword修改文件內(nèi)容呢?會立即寫入硬盤嗎?
Linux :也不會,我只是標(biāo)記這個Page cache “dirty”了, 然后我定期寫入硬盤。
內(nèi)存:你怎么能這樣!這不是欺騙那些程序嗎!那要是斷電怎么辦?
Linux:對于需要及時寫入硬盤的,有兩種辦法,一是調(diào)用我提供的fsync方法強(qiáng)制寫入硬盤,二是在訪問文件的時候,可以指定不用Page cache。
內(nèi)存:相當(dāng)于什么都沒說,不用Page cache 多慢啊。
Linux :你現(xiàn)在也知道page cache的重要性了吧。page cache 是一種比較通用的文件緩存機(jī)制,是我來管理的。有些應(yīng)用,比如數(shù)據(jù)庫,他需要更加靈活、更加復(fù)雜的文件緩存,那他就不用page cache ,自己另起爐灶了。
內(nèi)存:啊?數(shù)據(jù)庫也在把我當(dāng)作緩存?
Linux : 哈哈,是啊,要怪就怪硬盤吧,誰讓它那么慢!不過他要是和你一樣快,你小子就要下崗了,你想想,在一個訪問速度超快,容量超大,還不怕斷電威脅的存儲器面前,你是不是就變成渣渣了?
內(nèi)存嘆了一口氣: 好吧,我也管不了了,你們隨意折騰吧。
后記:本文的第一個圖和標(biāo)題來自
https://www.linuxatemyram.com/, 文章的內(nèi)容參考了https://manybutfinite.com/post/page-cache-the-affair-between-memory-and-files/ ,確切說,本文是加了料的改編版, 這個博客還有幾篇關(guān)于虛擬內(nèi)存的文章,非常棒,強(qiáng)烈建議大家去看看英文原文。
如需轉(zhuǎn)載,請通過作者微信公眾號coderising獲取授權(quán)