虛擬地址物理地址
虛擬地址物理地址
?學(xué)習(xí)啦小編整理了虛擬地址物理地址的相關(guān)資料。供大家參考!
虛擬地址和物理地址的概念
CPU通過地址來訪問內(nèi)存中的單元,地址有虛擬地址和物理地址之分,如果CPU沒有MMU(Memory Management Unit,內(nèi)存管理單元),或者有MMU但沒有啟用,CPU核在取指令或訪問內(nèi)存時(shí)發(fā)出的地址將直接傳到CPU芯片的外部地址引腳上,直接被內(nèi)存芯片(以下稱為物理內(nèi)存,以便與虛擬內(nèi)存區(qū)分)接收,這稱為物理地址(Physical Address,以下簡(jiǎn)稱PA),如下圖所示。
物理地址示意圖
如果CPU啟用了MMU,CPU核發(fā)出的地址將被MMU截獲,從CPU到MMU的地址稱為虛擬地址(Virtual Address,以下簡(jiǎn)稱VA),而MMU將這個(gè)地址翻譯成另一個(gè)地址發(fā)到CPU芯片的外部地址引腳上,也就是將虛擬地址映射成物理地址,如下圖所示[1]。
虛擬地址示意圖
MMU將虛擬地址映射到物理地址是以頁(yè)(Page)為單位的,對(duì)于32位CPU通常一頁(yè)為4K。例如,虛擬地址0xb700 1000~0xb700 1fff是一個(gè)頁(yè),可能被MMU映射到物理地址0x2000~0x2fff,物理內(nèi)存中的一個(gè)物理頁(yè)面也稱為一個(gè)頁(yè)框(Page Frame)。
內(nèi)核也不能直接訪問物理地址.但因?yàn)閮?nèi)核的虛擬地址和物理地址之間只是一個(gè)差值0xc0000000的區(qū)別,所以從物理地址求虛擬地址或從虛擬地址求物理地址很容易,+-這個(gè)差就行了
物理地址(physical address)
用于內(nèi)存芯片級(jí)的單元尋址,與處理器和CPU連接的地址總線相對(duì)應(yīng)。
——這個(gè)概念應(yīng)該是這幾個(gè)概念中最好理解的一個(gè),但是值得一提的是,雖然可以直接把物理地址理解成插在機(jī)器上那根內(nèi)存本身,把內(nèi)存看成一個(gè)從0字節(jié)一直到最大空量逐字節(jié)的編號(hào)的大數(shù)組,然后把這個(gè)數(shù)組叫做物理地址,但是事實(shí)上,這只是一個(gè)硬件提供給軟件的抽像,內(nèi)存的尋址方式并不是這樣。所以,說它是“與地址總線相對(duì)應(yīng)”,是更貼切一些,不過拋開對(duì)物理內(nèi)存尋址方式的考慮,直接把物理地址與物理的內(nèi)存一一對(duì)應(yīng),也是可以接受的。也許錯(cuò)誤的理解更利于形而上的抽像。
虛擬內(nèi)存(virtual memory)
這是對(duì)整個(gè)內(nèi)存(不要與機(jī)器上插那條對(duì)上號(hào))的抽像描述。它是相對(duì)于物理內(nèi)存來講的,可以直接理解成“不直實(shí)的”,“假的”內(nèi)存,例如,一個(gè)0x08000000內(nèi)存地址,它并不對(duì)就物理地址上那個(gè)大數(shù)組中0x08000000 - 1那個(gè)地址元素;
之所以是這樣,是因?yàn)楝F(xiàn)代操作系統(tǒng)都提供了一種內(nèi)存管理的抽像,即虛擬內(nèi)存(virtual memory)。進(jìn)程使用虛擬內(nèi)存中的地址,由操作系統(tǒng)協(xié)助相關(guān)硬件,把它“轉(zhuǎn)換”成真正的物理地址。這個(gè)“轉(zhuǎn)換”,是所有問題討論的關(guān)鍵。
有了這樣的抽像,一個(gè)程序,就可以使用比真實(shí)物理地址大得多的地址空間。(拆東墻,補(bǔ)西墻,銀行也是這樣子做的),甚至多個(gè)進(jìn)程可以使用相同的地址。不奇怪,因?yàn)檗D(zhuǎn)換后的物理地址并非相同的。
——可以把連接后的程序反編譯看一下,發(fā)現(xiàn)連接器已經(jīng)為程序分配了一個(gè)地址,例如,要調(diào)用某個(gè)函數(shù)A,代碼不是call A,而是call 0x0811111111 ,也就是說,函數(shù)A的地址已經(jīng)被定下來了。沒有這樣的“轉(zhuǎn)換”,沒有虛擬地址的概念,這樣做是根本行不通的。
Linux下獲取虛擬地址對(duì)應(yīng)的物理地址的方法
* /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
* 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)
* Bits 56-60 zero
* Bit 61 page is file-page or shared-anon
* Bit 62 page swapped
* Bit 63 page present
If the page is not present but in swap, then the PFN contains an
encoding of the swap file number and the page's offset into the
swap. Unmapped pages return a null PFN. This allows determining
precisely which pages are mapped (or in swap) and comparing mapped
pages between processes.
接下來,我們根據(jù)上述描述,給出獲取虛擬地址對(duì)應(yīng)的物理地址的代碼
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define page_map_file "/proc/self/pagemap"
#define PFN_MASK ((((uint64_t)1)<<55)-1)
#define PFN_PRESENT_FLAG (((uint64_t)1)<<63)
int mem_addr_vir2phy(unsigned long vir, unsigned long *phy)
{
int fd;
int page_size=getpagesize();
unsigned long vir_page_idx = vir/page_size;
unsigned long pfn_item_offset = vir_page_idx*sizeof(uint64_t);
uint64_t pfn_item;
fd = open(page_map_file, O_RDONLY);
if (fd<0)
{
printf("open %s failed", page_map_file);
return -1;
}
if ((off_t)-1 == lseek(fd, pfn_item_offset, SEEK_SET))
{
printf("lseek %s failed", page_map_file);
return -1;
}
if (sizeof(uint64_t) != read(fd, &pfn_item, sizeof(uint64_t)))
{
printf("read %s failed", page_map_file);
return -1;
}
if (0==(pfn_item & PFN_PRESENT_FLAG))
{
printf("page is not present");
return -1;
}
*phy = (pfn_item & PFN_MASK)*page_size + vir % page_size;
return 0;
}
如果擔(dān)心vir地址對(duì)應(yīng)的頁(yè)面不在內(nèi)存中,可以在調(diào)用mem_addr_vir2phy之前,先訪問一下此地址。
例如, int a=*(int *)(void *)vir;
如果擔(dān)心Linux的swap功能將進(jìn)程的頁(yè)面交換到硬盤上從而導(dǎo)致頁(yè)面的物理地址變化,可以關(guān)閉swap功能。
下面兩個(gè)C庫(kù)函數(shù)可以阻止Linux將當(dāng)前進(jìn)程的部分或全部頁(yè)面交換到硬盤上。
int mlock(const void *addr, size_t len);
int mlockall(int flags);
看過“虛擬地址空間映射到物理地址空間 ”的人還看了: