轉自:http://blog.csdn.net/kongkongkkk/article/details/74366200
如果讓你編寫一個程序,來獲取虛擬地址對應的物理地址。。你會試着操作MMU嗎。。→_→*
-
Linux文件目錄中的/proc記錄着當前進程的信息,稱其為虛擬文件系統。在/proc下有一個鏈接目錄名為self,這意味着哪一個進程打開了它,self中存儲的信息就是所鏈接進程的。self中有一個名為pagemap的文件,專門用來記錄所鏈接進程的物理頁號信息。這樣通過/proc/pid/pagemap文件,允許一個用戶態的進程查看到每個虛擬頁映射到的物理頁
-
/proc/pid/pagemap中的每一項都包含了一個64位的值,這個值內容如下所示。每一項的映射方式不同於真正的虛擬地址映射,其文件中遵循獨立的對應關系,即虛擬地址相對於0x0經過的頁面數是對應項在文件中的偏移量
* /proc/pid/pagemap. This file lets a userspace process find out which physical frame each virtual page is mapped to. It contains one 64-bit value for each virtual page, containing the following data (from fs/proc/task_mmu.c, above pagemap_read): * Bits 0-54 page frame number (PFN) if present//present為1時,bit0-54表示物理頁號 * Bits 0-4 swap type if swapped * Bits 5-54 swap offset if swapped * Bit 55 pte is soft-dirty (see Documentation/vm/soft-dirty.txt) * Bit 56 page exclusively mapped (since 4.2) * Bits 57-60 zero * Bit 61 page is file-page or shared-anon (since 3.5) * Bit 62 page swapped * Bit 63 page present//如果為1,表示當前物理頁在內存中;為0,表示當前物理頁不在內存中
1在計算物理地址時,只需要
找到虛擬地址的對應項,再通過對應項中的bit63判斷此物理頁是否在內存中,若在內存中則對應項中的物理頁號加上偏移地址,就能得到物理地址
-
通過程序獲取物理地址並驗證寫時拷貝技術
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <stdint.h> //計算虛擬地址對應的地址,傳入虛擬地址vaddr,通過paddr傳出物理地址 void mem_addr(unsigned long vaddr, unsigned long *paddr) { int pageSize = getpagesize();//調用此函數獲取系統設定的頁面大小 unsigned long v_pageIndex = vaddr / pageSize;//計算此虛擬地址相對於0x0的經過的頁面數 unsigned long v_offset = v_pageIndex * sizeof(uint64_t);//計算在/proc/pid/page_map文件中的偏移量 unsigned long page_offset = vaddr % pageSize;//計算虛擬地址在頁面中的偏移量 uint64_t item = 0;//存儲對應項的值 int fd = open("/proc/self/pagemap", O_RDONLY);。。以只讀方式打開/proc/pid/page_map if(fd == -1)//判斷是否打開失敗 { printf("open /proc/self/pagemap error\n"); return; } if(lseek(fd, v_offset, SEEK_SET) == -1)//將游標移動到相應位置,即對應項的起始地址且判斷是否移動失敗 { printf("sleek error\n"); return; } if(read(fd, &item, sizeof(uint64_t)) != sizeof(uint64_t))//讀取對應項的值,並存入item中,且判斷讀取數據位數是否正確 { printf("read item error\n"); return; } if((((uint64_t)1 << 63) & item) == 0)//判斷present是否為0 { printf("page present is 0\n"); return ; } uint64_t phy_pageIndex = (((uint64_t)1 << 55) - 1) & item;//計算物理頁號,即取item的bit0-54 *paddr = (phy_pageIndex * pageSize) + page_offset;//再加上頁內偏移量就得到了物理地址 } const int a = 100;//全局常量 int main() { int b = 100;//局部變量 static c = 100;//局部靜態變量 const int d = 100;//局部常量 char *str = "Hello World!"; unsigned long phy = 0;//物理地址 char *p = (char*)malloc(100);//動態內存 int pid = fork();//創建子進程 if(pid == 0) { //p[0] = '1';//子進程中修改動態內存 mem_addr((unsigned long)&a, &phy); printf("pid = %d, virtual addr = %x , physical addr = %x\n", getpid(), &a, phy); } else { mem_addr((unsigned long)&a, &phy); printf("pid = %d, virtual addr = %x , physical addr = %x\n", getpid(), &a, phy); } sleep(100); free(p); waitpid(); return 0; }
-
測試結果如下:
-
全局常量:符合寫時拷貝技術
-
局部變量:不符合寫時拷貝技術。原因分析,有可能是物理頁上的其他數據被改動,導致拷貝出一個新物理頁面
-
局部靜態變量:不符合寫時拷貝技術。原因分析,有可能是物理頁上的其他數據被改動,導致拷貝出一個新物理頁面
-
局部常量:不符合寫時拷貝技術。原因分析,有可能是物理頁上的其他數據被改動,導致拷貝出一個新物理頁面
-
字符串:符合寫時拷貝技術
-
動態內存:符合寫時拷貝技術
子進程不修改動態內存
子進程修改動態內存
-
*其實想要知道虛擬地址對應的物理地址,通過這樣的方式也可以得到物理地址而不用操作MMU。。。*