淺析邏輯地址與物理地址映射關係

基本概念:

邏輯地址:在具有地址變換功能的計算機中,訪問指令給出的操作數。

物理地址:用於內存芯片級單元尋址,與CPU連接的地址總線相對應。

線性地址:邏輯地址和物理地址轉換的中間層,即硬件平臺頁式轉換前的地址。

我們都知道任何一個獨立運行的程序都需要系統分配單獨的內存空間,大多數情況下這個工作是由系統完成,方便程序訪問變量,程序不需要關心變量的物理地址。因此現代操作系統都提供了一種內存管理的抽象,即虛擬內存。進程使用虛擬內存的邏輯地址範文,操作系統協助轉換成真正的物理地址。

虛擬存儲空間分佈

在32位的處理器平臺上,linux的存儲空間和虛擬存儲空間的地址範圍都是從 0x00000000 到 0xFFFFFFFF 共4G,但是物理存儲空間和虛擬存儲空間佈局完全不同。


每個進程能分配到除1G的內核空間以外的3G的獨立內存空間,代碼段、數據段和堆是從低地址向高地址,棧從高地址向低地址把實際的物理內存按4K的大小爲一個單元標號,叫做頁。

實模式和保護模式

CPU有三種工作方式:實模式、保護模式和虛擬8086模式。在剛啓動的時候是實模式,只能訪問1M以下的內存;操作系統運行起來就切換到保護模式,全部的32條地址總線有效,可尋址4G的物理地址空間。

以8086爲例,運算器(ALU)的最大尋址數爲16,地址總線爲20,內存容量爲1M。尋址方式爲 物理地址 = 基地址 +偏移量,
基地址 = 16位的段地址 << 4;
以80386爲例,CPU 和地址總線的大小相同,內存容量爲4G。尋址方式  線性地址 = 基地址 + 偏移量(邏輯地址 ),一般情況下邏輯地址和線性地址一致,通過Page_Tables得到物理地址。

頁式管理系統地址轉換

linux中的存放虛擬內存_物理內存頁的對應關係是在主目錄下的 /proc/pid/pagemap 文件,下圖表示頁式管理系統的地址轉換方式,p 表示邏輯頁號,f 表示物理頁號,d表示頁內地址。


頁表的作用是實現從邏輯頁號到物理頁號的地址映射,以邏輯地址的檢索頁號檢索頁表得到該頁的物理頁號;同時將頁內地址d
和物理頁號拼接成實際的物理地址。

用c語言實現邏輯地址轉換成物理地址

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>

int add(int a,int b)
{
    return 0;
}

int g = 10;


/*
*  邏輯地址 映射得到 物理地址
*  參數爲 邏輯地址
*/
uint64_t vir_to_phy(unsigned long int vaddr)
{
     int pagesize = getpagesize();
     int index =  vaddr/pagesize;//邏輯頁號   p
     int offset = vaddr%pagesize;//頁內偏移量 d
     
     int fileoffset = index*8;//pagemap文件中讀出 物理頁號              
     int fd = open("/proc/self/pagemap",O_RDONLY);
     if(fd == -1)
     {
		return 0;
     }
     lseek(fd,fileoffset,SEEK_SET);
     uint64_t val = 0;
     read(fd,&val,sizeof(val));

     uint64_t phy = 0;
     if(val & (uint64_t)1<<63)
     {
		val &= (((uint64_t)1<<55)-1);
		phy = val*pagesize+offset;//拼接物理地址
		return phy;
     }
     return 0;
}

int main()
{
   
    int a = 0;
    printf("局部變量   a = %x\n",&a);
    printf("全局變量   g = %x\n",&g);
    printf("全局函數   add = %x\n",add);
    printf("主程序入口 main = %x\n",main);
    
    int *p = (int*)malloc(1024*1024*1024);//申請1G的資源   
    pid_t pid = fork();
    if(pid == 0)
    {
	//memset(p,0,1024*1024*1024);
    }
	//父子進程中的映射關係
    int b = vir_to_phy(&p);
    printf("virtul   a = %x\n",&p);
    printf("physical a = %x\n",b);
    free(p);
    
    exit(0);
}
運行程序我們發現,申請的資源沒有使用時,父子進程返回的邏輯地址、物理地址均相同;在對子進程申請的內存賦值後,父子進程返回的物理地址不相同。也就是說,在進行fork()複製後父子進程是共享內存的,但當其中一個進程去修改其中一個數據結構時,系統會產生缺頁中斷,爲其分配全新的空間。


參考資料:



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章