Linux下訪問匿名頁發(fā)生的神奇“化學(xué)反應(yīng)”
時(shí)間:2021-09-29 15:22:35
手機(jī)看文章
掃描二維碼
隨時(shí)隨地手機(jī)看文章
[導(dǎo)讀]首先祝大家中秋節(jié)快樂,闔家歡樂,節(jié)日之余記得學(xué)習(xí)喲!Linux中有后備文件支持的頁稱為文件頁,如屬于進(jìn)程的代碼段、數(shù)據(jù)段的頁,內(nèi)存回收的時(shí)候這些頁面只需要做臟頁的同步即可(干凈的頁面可以直接丟棄掉)。反之為匿名頁,如進(jìn)程的堆棧使用的頁,內(nèi)存回收的時(shí)候這些頁面不能簡單的丟棄掉,需要交換到交換分區(qū)或交換文件。本文中,主要分析匿名頁的訪問將發(fā)生哪些可能顛覆我們認(rèn)知的"化學(xué)反應(yīng)"。1.實(shí)例代碼首先以一個(gè)簡單的示例代碼來說明:#include?#include?#include?#include?#include?#define?MAP_SIZE?(100?*?1024?*?1024)int?main(...
首先祝大家中秋節(jié)快樂,闔家歡樂,節(jié)日之余記得學(xué)習(xí)喲!Linux中有后備文件支持的頁稱為文件頁,如屬于進(jìn)程的代碼段、數(shù)據(jù)段的頁,內(nèi)存回收的時(shí)候這些頁面只需要做臟頁的同步即可(干凈的頁面可以直接丟棄掉)。反之為匿名頁,如進(jìn)程的堆棧使用的頁,內(nèi)存回收的時(shí)候這些頁面不能簡單的丟棄掉,需要交換到交換分區(qū)或交換文件。本文中,主要分析匿名頁的訪問將發(fā)生哪些可能顛覆我們認(rèn)知的"化學(xué)反應(yīng)"。
1.實(shí)例代碼
首先以一個(gè)簡單的示例代碼來說明:#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;
}
代碼非常簡單:首先通過mmap分配100M的私有可讀可寫匿名頁面,然后進(jì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í)候,我們還沒有開始通過mmap分配內(nèi)存,此時(shí)free命令輸出作為參考。第二次提示執(zhí)行free命令的時(shí)候,我們已經(jīng)通過mmap分配了100M的內(nèi)存,此時(shí)發(fā)現(xiàn)free命令輸出內(nèi)存消耗基本沒有變化。第三次提示執(zhí)行free命令的時(shí)候,我們對于分配的匿名頁面進(jìn)行了讀操作,此時(shí)發(fā)現(xiàn)free命令輸出內(nèi)存消耗頁基本沒有變化,?這基本上會顛覆我們的認(rèn)知。第四次提示執(zhí)行free命令的時(shí)候,我們對于分配的匿名頁面進(jìn)行了寫操作,此時(shí)發(fā)現(xiàn)free命令輸出內(nèi)存消耗大概為100M。2.內(nèi)核原理
下面我們從Linux內(nèi)核的層面來解析發(fā)生以上神奇現(xiàn)象的原理。2.1 mmap的內(nèi)存消耗
mmap申請匿名頁的時(shí)候,只是申請了虛擬內(nèi)存(通過vm_area_struct結(jié)構(gòu)來描述,如描述虛擬內(nèi)存區(qū)域的地址范圍、訪問權(quán)限等,以下簡稱vma),實(shí)際的物理內(nèi)存并沒有申請(除了用于管理虛擬內(nèi)存區(qū)域的vma等結(jié)構(gòu)內(nèi)存的申請),當(dāng)前虛擬內(nèi)存和物理內(nèi)存并沒有建立頁表映射關(guān)系,而真正的申請的匿名頁所對應(yīng)的物理頁在實(shí)際訪問的時(shí)候按需分配獲得,所以此時(shí)我們看不到內(nèi)存的消耗情況。2.2 第一次讀匿名頁的內(nèi)存消耗
通過mmap申請完虛擬內(nèi)存之后,進(jìn)程就可以按照之前申請vma的訪問權(quán)限進(jìn)行訪問,第一發(fā)生讀訪問,這個(gè)時(shí)候由于虛擬內(nèi)存和物理內(nèi)存并沒有建立頁表映射關(guān)系,通過虛擬地址并不能查找到物理內(nèi)存,所以會發(fā)生處理器的異常,最終分析是因?yàn)閿?shù)據(jù)訪問異常導(dǎo)致,就由處理器架構(gòu)相關(guān)的代碼進(jìn)入了我們通用的缺頁異常處理例程中。缺頁異常調(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
缺頁異常進(jìn)入handle_pte_fault后,在1標(biāo)簽代碼處,來判斷訪問的虛擬內(nèi)存頁的頁表項(xiàng)是否為空,為空說明這個(gè)這個(gè)虛擬頁沒有和物理頁建立映射關(guān)系。然后在2標(biāo)簽代碼處判斷是否為匿名頁缺頁異常(實(shí)際上是判斷是否為私有的匿名頁,當(dāng)前當(dāng)前示例代碼場景申請的為私有匿名頁面)。在3標(biāo)簽代碼處,進(jìn)行真正的私有匿名頁缺頁異常處理。下面主要看下第一次讀匿名頁的處理:do_anonymous_page
->pte_alloc(vma->vm_mm,?vmf->pmd)???-------------------?1
->/*?Use?the?zero-page?for?reads?*/
if?(!(vmf->flags?