Linux下訪問(wèn)匿名頁(yè)發(fā)生的神奇“化學(xué)反應(yīng)”
時(shí)間:2021-09-29 15:22:35
手機(jī)看文章
掃描二維碼
隨時(shí)隨地手機(jī)看文章
[導(dǎo)讀]首先祝大家中秋節(jié)快樂(lè),闔家歡樂(lè),節(jié)日之余記得學(xué)習(xí)喲!Linux中有后備文件支持的頁(yè)稱為文件頁(yè),如屬于進(jìn)程的代碼段、數(shù)據(jù)段的頁(yè),內(nèi)存回收的時(shí)候這些頁(yè)面只需要做臟頁(yè)的同步即可(干凈的頁(yè)面可以直接丟棄掉)。反之為匿名頁(yè),如進(jìn)程的堆棧使用的頁(yè),內(nèi)存回收的時(shí)候這些頁(yè)面不能簡(jiǎn)單的丟棄掉,需要交換到交換分區(qū)或交換文件。本文中,主要分析匿名頁(yè)的訪問(wèn)將發(fā)生哪些可能顛覆我們認(rèn)知的"化學(xué)反應(yīng)"。1.實(shí)例代碼首先以一個(gè)簡(jiǎn)單的示例代碼來(lái)說(shuō)明:#include?#include?#include?#include?#include?#define?MAP_SIZE?(100?*?1024?*?1024)int?main(...
首先祝大家中秋節(jié)快樂(lè),闔家歡樂(lè),節(jié)日之余記得學(xué)習(xí)喲!Linux中有后備文件支持的頁(yè)稱為文件頁(yè),如屬于進(jìn)程的代碼段、數(shù)據(jù)段的頁(yè),內(nèi)存回收的時(shí)候這些頁(yè)面只需要做臟頁(yè)的同步即可(干凈的頁(yè)面可以直接丟棄掉)。反之為匿名頁(yè),如進(jìn)程的堆棧使用的頁(yè),內(nèi)存回收的時(shí)候這些頁(yè)面不能簡(jiǎn)單的丟棄掉,需要交換到交換分區(qū)或交換文件。本文中,主要分析匿名頁(yè)的訪問(wèn)將發(fā)生哪些可能顛覆我們認(rèn)知的"化學(xué)反應(yīng)"。
1.實(shí)例代碼
首先以一個(gè)簡(jiǎn)單的示例代碼來(lái)說(shuō)明:#include?
#include?
#include?
#include?
#include?
#define?MAP_SIZE?(100?*?1024?*?1024)
int?main(int?argc,?char?*argv[])
{
?char?*p;
?char?val;
?int?i;
?puts("before?mmap?ok,?pleace?exec?'free?-m'!");
?sleep(5);
?//mmap
?p?=?mmap(NULL,?MAP_SIZE,?PROT_READ?|?PROT_WRITE,?MAP_PRIVATE?|?MAP_ANONYMOUS,?-1,?0);
?if(p?==?NULL)?{
??perror("fail?to?malloc");
??return?-1;
?}?
?puts("after?mmap?ok,?pleace?exec?'free?-m'!");
?sleep(5);
?//read
?for?(i?=?0;?i???val?=?p[i];
?}
?puts("read?ok,?pleace?exec?'free?-m'!");
?sleep(5);
#if?1
?//write
?memset(p,?0x55,?MAP_SIZE);
?puts("write?ok,?pleace?exec?'free?-m'!");
#endif
?//sleep
?pause();
?return?0;
}
代碼非常簡(jiǎn)單:首先通過(guò)mmap分配100M的私有可讀可寫匿名頁(yè)面,然后進(jìn)行讀寫訪問(wèn),分別在提示的時(shí)候在另外一個(gè)窗口執(zhí)行free -m命令查看輸出結(jié)果。程序執(zhí)行結(jié)果如下:$?./anon_rw_demo
before?mmap?ok,?pleace?exec?'free?-m'!
after?mmap?ok,?pleace?exec?'free?-m'!
read?ok,?pleace?exec?'free?-m'!
write?ok,?pleace?exec?'free?-m'!
命令執(zhí)行結(jié)果如下:$?free?-m
??????????????總計(jì)?????????已用????????空閑??????共享????緩沖/緩存????可用
內(nèi)存:?????? 15729 ?????? 8286 ?????? 1945 ??????? 895 ?????? 5497 ?????? 6220
交換:?????? 16290??????? 1599 ????? 14691
$?free?-m
??????????????總計(jì)?????????已用????????空閑??????共享????緩沖/緩存????可用
內(nèi)存:?????? 15729 ?????? 8286 ?????? 1945 ??????? 895 ?????? 5497 ?????? 6220
交換:?????? 16290??????? 1599 ????? 14691
$?free?-m
??????????????總計(jì)?????????已用????????空閑??????共享????緩沖/緩存????可用
內(nèi)存:?????? 15729 ?????? 8286 ?????? 1945 ??????? 895 ?????? 5497 ?????? 6220
交換:?????? 16290??????? 1599 ????? 14691
$?free?-m
??????????????總計(jì)?????????已用????????空閑??????共享????緩沖/緩存????可用
內(nèi)存:?????? 15729 ?????? 8383 ?????? 1848 ??????? 895 ?????? 5497 ?????? 6123
交換:?????? 16290??????? 1599 ????? 14691
可以看到:第一次提示執(zhí)行free命令的時(shí)候,我們還沒(méi)有開(kāi)始通過(guò)mmap分配內(nèi)存,此時(shí)free命令輸出作為參考。第二次提示執(zhí)行free命令的時(shí)候,我們已經(jīng)通過(guò)mmap分配了100M的內(nèi)存,此時(shí)發(fā)現(xiàn)free命令輸出內(nèi)存消耗基本沒(méi)有變化。第三次提示執(zhí)行free命令的時(shí)候,我們對(duì)于分配的匿名頁(yè)面進(jìn)行了讀操作,此時(shí)發(fā)現(xiàn)free命令輸出內(nèi)存消耗頁(yè)基本沒(méi)有變化,?這基本上會(huì)顛覆我們的認(rèn)知。第四次提示執(zhí)行free命令的時(shí)候,我們對(duì)于分配的匿名頁(yè)面進(jìn)行了寫操作,此時(shí)發(fā)現(xiàn)free命令輸出內(nèi)存消耗大概為100M。2.內(nèi)核原理
下面我們從Linux內(nèi)核的層面來(lái)解析發(fā)生以上神奇現(xiàn)象的原理。2.1 mmap的內(nèi)存消耗
mmap申請(qǐng)匿名頁(yè)的時(shí)候,只是申請(qǐng)了虛擬內(nèi)存(通過(guò)vm_area_struct結(jié)構(gòu)來(lái)描述,如描述虛擬內(nèi)存區(qū)域的地址范圍、訪問(wèn)權(quán)限等,以下簡(jiǎn)稱vma),實(shí)際的物理內(nèi)存并沒(méi)有申請(qǐng)(除了用于管理虛擬內(nèi)存區(qū)域的vma等結(jié)構(gòu)內(nèi)存的申請(qǐng)),當(dāng)前虛擬內(nèi)存和物理內(nèi)存并沒(méi)有建立頁(yè)表映射關(guān)系,而真正的申請(qǐng)的匿名頁(yè)所對(duì)應(yīng)的物理頁(yè)在實(shí)際訪問(wèn)的時(shí)候按需分配獲得,所以此時(shí)我們看不到內(nèi)存的消耗情況。2.2 第一次讀匿名頁(yè)的內(nèi)存消耗
通過(guò)mmap申請(qǐng)完虛擬內(nèi)存之后,進(jìn)程就可以按照之前申請(qǐng)vma的訪問(wèn)權(quán)限進(jìn)行訪問(wèn),第一發(fā)生讀訪問(wèn),這個(gè)時(shí)候由于虛擬內(nèi)存和物理內(nèi)存并沒(méi)有建立頁(yè)表映射關(guān)系,通過(guò)虛擬地址并不能查找到物理內(nèi)存,所以會(huì)發(fā)生處理器的異常,最終分析是因?yàn)閿?shù)據(jù)訪問(wèn)異常導(dǎo)致,就由處理器架構(gòu)相關(guān)的代碼進(jìn)入了我們通用的缺頁(yè)異常處理例程中。缺頁(yè)異常調(diào)用鏈如下:"mm/memory.c"
處理器架構(gòu)相關(guān)異常處理代碼
->?handle_mm_fault
????->?__handle_mm_fault
????????->?handle_pte_fault
????????????->??if?(!vmf->pte)?{???-------------------?1
?????????????????????if?(vma_is_anonymous(vmf->vma))??-------------------?2
?????????????????????????????return?do_anonymous_page(vmf);???-------------------?3
缺頁(yè)異常進(jìn)入handle_pte_fault后,在1標(biāo)簽代碼處,來(lái)判斷訪問(wèn)的虛擬內(nèi)存頁(yè)的頁(yè)表項(xiàng)是否為空,為空說(shuō)明這個(gè)這個(gè)虛擬頁(yè)沒(méi)有和物理頁(yè)建立映射關(guān)系。然后在2標(biāo)簽代碼處判斷是否為匿名頁(yè)缺頁(yè)異常(實(shí)際上是判斷是否為私有的匿名頁(yè),當(dāng)前當(dāng)前示例代碼場(chǎng)景申請(qǐng)的為私有匿名頁(yè)面)。在3標(biāo)簽代碼處,進(jìn)行真正的私有匿名頁(yè)缺頁(yè)異常處理。下面主要看下第一次讀匿名頁(yè)的處理:do_anonymous_page
->pte_alloc(vma->vm_mm,?vmf->pmd)???-------------------?1
->/*?Use?the?zero-page?for?reads?*/
if?(!(vmf->flags?