LWN:讓vma的匿名頁(yè)不再匿名!
關(guān)注了就能看到更多這么棒的文章哦~
Not-so-anonymous virtual memory areas
By Jonathan Corbet
September 3, 2021
DeepL assisted translation
https://lwn.net/Articles/867818/
計(jì)算機(jī)術(shù)語(yǔ)的名字有些時(shí)候是不那么直觀的。但就算是業(yè)內(nèi)的長(zhǎng)期參與人員可能也會(huì)對(duì) anonymous memory 這個(gè)名字也會(huì)覺(jué)得有些費(fèi)解。Suren Baghdasaryan 發(fā)布了一組 patch set 正是希望讓 anonymous memory 不那么匿名。目前看來(lái),有一些開(kāi)發(fā)者認(rèn)為這個(gè)想法很有用,不僅可以幫助克服一上來(lái)的認(rèn)知偏差,而且還又復(fù)活了一個(gè) 8 年前的 patch 從而將其納入內(nèi)核。
用戶(hù)空間(user space)使用的內(nèi)存分為兩大類(lèi):有 file-backed(有對(duì)應(yīng)文件的)和 anonymous(匿名的)。file-backed page 這些內(nèi)存頁(yè)都是與持久性存儲(chǔ)中的一個(gè)文件中的 page 有著直接對(duì)應(yīng)的關(guān)系。當(dāng)該 page 尚未被污染時(shí),其內(nèi)容與磁盤(pán)上的內(nèi)容完全相同。相應(yīng)地,一個(gè)匿名頁(yè)(anonymous page)與文件系統(tǒng)中的文件就沒(méi)有關(guān)聯(lián)了。這些 page 用來(lái)存放進(jìn)程中的數(shù)據(jù)區(qū)、堆棧等。如果一個(gè)匿名頁(yè)必須得要寫(xiě)入持久性存儲(chǔ)了(通常是為了回收這個(gè) page 來(lái)挪作其他用途),那就必須要在 swap 區(qū)域先分配好空間來(lái)存放其對(duì)應(yīng)內(nèi)容。
每個(gè)進(jìn)程中是有對(duì)應(yīng)文件的 memory page 更多呢,還是匿名頁(yè)更多呢?這在不同的工作場(chǎng)景下是不一樣的。很多情況下進(jìn)程中大部分 page 都是匿名頁(yè)。尤其是在大量的云計(jì)算客戶(hù)端的工作場(chǎng)景中更加是這樣,這些客戶(hù)端程序往往不怎么使用本地文件。安卓設(shè)備就是一個(gè)典型的例子。如果有人試圖針對(duì)這種工作場(chǎng)景的內(nèi)存使用情況進(jìn)行優(yōu)化,那么匿名頁(yè)就會(huì)給他們帶來(lái)更多挑戰(zhàn)。因?yàn)?page 是匿名的,也就是沒(méi)有它們是如何被創(chuàng)建的這些信息,所以很難知道各個(gè)匿名頁(yè)都被用來(lái)做什么了。
這一點(diǎn)還是可以改善的,那就是讓匿名頁(yè)面變得不那么匿名。如果能夠知道是用戶(hù)空間的哪個(gè)子系統(tǒng)或函數(shù)庫(kù)創(chuàng)建的這個(gè) page,就會(huì)更容易弄清楚誰(shuí)是最主要的使用者。例如系統(tǒng)中如果記錄了有多少匿名頁(yè)是由 jemalloc 庫(kù)創(chuàng)建的這個(gè)信息,就可以幫助確定對(duì) jemalloc 的使用是不是一個(gè)我們應(yīng)該優(yōu)先優(yōu)化的目標(biāo)。然而,Linux 系統(tǒng)并不容易(甚至不可能)獲得這類(lèi)信息。
要想讓情況變得更好,就需要從用戶(hù)空間獲得一些配合,因?yàn)閮?nèi)核不可能知道具體是用戶(hù)空間的哪個(gè)子系統(tǒng)正在分配當(dāng)前這個(gè) page。這組 patch set 的核心就是 Colin Cross 的一個(gè)用來(lái)幫助完成這個(gè)工作的 patch,它最早在 2013 年就發(fā)布出來(lái)了,增加了一個(gè)新的 prctl() 操作。
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start, len, name);
這個(gè)函數(shù)會(huì)把 name 跟從 start 開(kāi)始的 len 長(zhǎng)度的匿名頁(yè)關(guān)聯(lián)起來(lái)。實(shí)際上這個(gè) name 是跟用來(lái)描述這一段內(nèi)存范圍的虛擬內(nèi)存區(qū)域(VMA)結(jié)構(gòu)關(guān)聯(lián)起來(lái)的。因此,實(shí)際上所有屬于給定范圍內(nèi)的 VMA 的 page 內(nèi)的分配都將得到這個(gè) name,即使這些 page 本身不在這個(gè)范圍內(nèi)。每個(gè) mmap() 調(diào)用通常會(huì)創(chuàng)建一個(gè) VMA(盡管有一些情況會(huì)比較復(fù)雜),所以與任何指定 VMA 相關(guān)的所有 page 通常都是按同樣的方式創(chuàng)建的。
每個(gè)進(jìn)程的 /proc 目錄下的 maps 和 smaps 文件中已經(jīng)包含了很多關(guān)于該進(jìn)程的 VMA 信息。在合入了這組 patch 之后,這些文件也將包含與 anonymous VMA 相關(guān)聯(lián)的 name(如果之前 name 已經(jīng)關(guān)聯(lián)上來(lái)的話(huà))。在接受這個(gè) name 之前,會(huì)先檢查這個(gè) name 是否可以被 print 出來(lái)。系統(tǒng)工具就可以利用這些信息來(lái)將 page 與這些 name 關(guān)聯(lián)起來(lái),從而能跟創(chuàng)建這些 page 的子系統(tǒng)關(guān)聯(lián)起來(lái)。
給 VMA 分配一個(gè)名字看起來(lái)并不困難,但事實(shí)上這是這個(gè) patch 中最棘手的部分。一個(gè)系統(tǒng)中可以有很多進(jìn)程,每個(gè)進(jìn)程都可以有很多 VMA,所以這些 name 的管理方面需要能很容易地?cái)U(kuò)展。早期版本的 patch set 曾嘗試直接指向用戶(hù)空間所所提供的 name,這樣就不需要在內(nèi)核中分配內(nèi)存了,但是,正如 Kees Cook 所指出的,它也帶來(lái)了一些有意思的 security 問(wèn)題。當(dāng)時(shí) Cook 建議直接將字符串復(fù)制到內(nèi)核空間。
雖然復(fù)制字符串的做法是可行的,但仍有一個(gè)小問(wèn)題:當(dāng)一個(gè)進(jìn)程進(jìn)行 fork 時(shí),它的 VMA 會(huì)被復(fù)制給新的子進(jìn)程所用。那么現(xiàn)在所有這些 name 字符串也必須要被復(fù)制了。Baghdasaryan 做了一個(gè)最壞情況的測(cè)試,也就是用一個(gè)進(jìn)程創(chuàng)建了 64000 個(gè) VMA,給每個(gè) VMA 分配了一個(gè)很長(zhǎng)的 name,然后調(diào)用 fork(),結(jié)果是性能下降了近 40%。即使這種情況在現(xiàn)實(shí)世界的工作場(chǎng)景中應(yīng)該不會(huì)出現(xiàn),但這種下降幅度已經(jīng)足以引起人們的關(guān)注了。
為了避免得到過(guò)得的抱怨,Baghdasaryan 增加了一個(gè)機(jī)制來(lái)實(shí)現(xiàn) name 的公用機(jī)制,并且?guī)в幸糜?jì)數(shù)。這樣一來(lái)現(xiàn)在 fork() 調(diào)用就只需要增加引用計(jì)數(shù)而已,不用再分配內(nèi)存以及復(fù)制字符串了。采用了這個(gè)新增機(jī)制之后,最壞情況下,性能降幅 "減少到了原來(lái)降幅的 1/3 ~ 1/4",而且據(jù)說(shuō)在那些合理的測(cè)試場(chǎng)景下都看不到有下降了。
這個(gè)功能顯然是很有用的,安卓已經(jīng)使用這個(gè)機(jī)制很多年了,它一直帶著之前最早期的那個(gè)版本的 patch。到目前為止,review 反饋意見(jiàn)都集中在一些較小問(wèn)題上——例如 name 中可以使用哪些字符。因此,看起來(lái)要合入這組 patch set 沒(méi)有太多困難需要解決了。對(duì)于這個(gè)功能來(lái)說(shuō),八年的等待應(yīng)該足夠了,anonymous page 可能很快就會(huì)不再那么
anonymous 了。
全文完
LWN 文章遵循 CC BY-SA 4.0 許可協(xié)議。
長(zhǎng)按下面二維碼關(guān)注,關(guān)注 LWN 深度文章以及開(kāi)源社區(qū)的各種新近言論~
