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