Linux 虛擬地址與物理地址的映射關系分析【轉】


 
 

Ordeder原創文章,原文鏈接: http://blog.csdn.net/ordeder/article/details/41630945

源碼版本 2.4.0

1. 虛擬空間

 

0-3G 用戶空間  0x00000000  ~ 0xbfffffff

3-4G 內核空間     0xc0000000 ~ 0xffffffff

每個用戶進程都有獨立的用戶空間(虛擬地址0-3),而內核空間是唯一的(相當於共享)

每個進程的用戶空間用mm_struct描述,即task_struct.mm。

2.進程虛擬地址的組織

2.1 虛擬空間、用戶空間

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. struct mm_struct {  
  2.     struct vm_area_struct * mmap;   /* list of VMAs */  
  3.     ...  
  4.     pgd_t * pgd;                //用於地址映射  
  5.     atomic_t mm_users;          /* How many users with user space? */  
  6.     atomic_t mm_count;          /* How many references to "struct mm_struct" (users count as 1) */  
  7.     int map_count;              /* number of VMAs */  
  8.     ...  
  9.     //描述用戶空間的段分布:數據段,代碼段,堆棧段  
  10.     unsigned long start_code, end_code, start_data, end_data;  
  11.     unsigned long start_brk, brk, start_stack;  
  12.     unsigned long arg_start, arg_end, env_start, env_end;  
  13.     unsigned long rss, total_vm, locked_vm;  
  14.     ...  
  15. };  
以上結構描述了進程的用戶空間的結構,其中
pgd_t    是該進程用戶空間地址映射到物理地址時使用
vm_area_struct 是進程用戶空間已映射到物理空間的虛擬地址區間,mmap是該空間區塊組成的鏈表。

虛擬空間的空洞:虛擬空間還未被映射的區塊(即沒有被使用),那么就沒有vm_area_struct結構

2.2 內存區間

 

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. /* 
  2.  * This struct defines a memory VMM memory area. There is one of these 
  3.  * per VM-area/task.  A VM area is any part of the process virtual memory 
  4.  * space that has a special rule for the page-fault handlers (ie a shared 
  5.  * library, the executable area etc). 
  6.  */  
  7. struct vm_area_struct {  
  8.     struct mm_struct * vm_mm;   /* VM area parameters */  
  9.     unsigned long vm_start;     //虛擬空間起始地址  
  10.     unsigned long vm_end;       //終止地址  
  11.   
  12.     /* linked list of VM areas per task, sorted by address */  
  13.     struct vm_area_struct *vm_next;  
  14.       
  15.     //該區間的權限及標志  
  16.     pgprot_t vm_page_prot;  
  17.     unsigned long vm_flags;  
  18.       
  19.     //一些vm_area 的鏈接  
  20.     ...  
  21.     struct vm_operations_struct * vm_ops;  
  22.     unsigned long vm_pgoff;     /* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */  
  23.     struct file * vm_file;      //用於將磁盤文件映射至用戶空間  
  24.     ...  
  25. };  

虛擬空間區間的描述中:
vm_start/vm_end    為該區塊的起始和結束地址
vm_file    是在文件映射中使用到,即常用的mmap(fd,...)函數,簡單說即將虛擬空間映射至文件在內核的緩沖區,那么這時候訪問該虛擬空間將有別於pgd的映射。
vm_operations_struct 為本虛擬區間的操作,其中的nopage函數指針是處理內存缺頁而使用的。對於通用的內存映射,該缺頁處理函數為do_no_page()將虛擬地址映射到物理地址(匿名映射):分配物理頁& 設置pgd & pte。
而對於mmap操作相關的虛擬地址,其缺頁處理函數將和文件系統的缺頁函數相關,filemap_nopage(),通過文件系統的缺頁從磁盤將相關文件塊加載如內核緩沖區.

 

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. struct vm_operations_struct {  
  2.     void (*open)(struct vm_area_struct * area);  
  3.     void (*close)(struct vm_area_struct * area);  
  4.     struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);     //缺頁操作  
  5. };  


3.系統物理地址的組織

內核將物理地址按頁來組織,struct page描述系統的物理頁的信息,但是頁的數據內容是不在該結構中的。系統有全局數據 struct page mem_map[],用於記錄每個物理頁。
頁面大小為4kb,在源碼中用體現為(PAGE_SHIFT = 12)

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. /* 
  2.  * Try to keep the most commonly accessed fields in single cache lines 
  3.  * here (16 bytes or greater).  This ordering should be particularly 
  4.  * beneficial on 32-bit processors. 
  5.  * 
  6.  * The first line is data used in page cache lookup, the second line 
  7.  * is used for linear searches (eg. clock algorithm scans).  
  8.  */  
  9. typedef struct page {  
  10.     struct list_head list;  
  11.     struct address_space *mapping;  
  12.     unsigned long index;  
  13.     struct page *next_hash;  
  14.     atomic_t count;  
  15.     unsigned long flags;    /* atomic flags, some possibly updated asynchronously */  
  16.     struct list_head lru;  
  17.     unsigned long age;  
  18.     wait_queue_head_t wait;  
  19.     struct page **pprev_hash;  
  20.     struct buffer_head * buffers;  
  21.     void *virtual; /* non-NULL if kmapped */  
  22.     struct zone_struct *zone;  
  23. } mem_map_t;  

 

struct page是用於描述一個物理頁面,該結構僅僅是作為描述,也就是說該頁面的4kb數據時存儲於某個連續的4kb的物理空間(由MMU決定,具體見下文)。其中:
lru    頁面緩沖的調度策略(最少使用優先)

題外話:
page也可以用於文件緩沖,相關參數及作用:
buffer_head    是和設備文件相關的操作,例如在文件系統中,file的一個page有4個塊,這些塊就存儲於buffer_head鏈表指定的內存中。
index 在文件系統中是用於file緩沖的頁號。

 

 

3.1 用戶空間頁面目錄(映射關系)

 

進程的虛擬空間描述中,pgd是用於頁式存儲的映射使用。當內核發生進程切換時,將新進程的pgd載入CR3寄存器,CPU中的MMU單元依據CR3寄存器進行頁面映射。

pgd,pmd和pte可以看做是數組,為進程的地址空間到物理空間實現映射。其中虛擬地址的高位地址決定pgd,中間段地址決定pmd,而低位地址決定pte,pte是“page table entry”。
最終定位的pte中存放的即為對應物理頁面的指針。
[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. typedef struct { unsigned long pte; } pte_t;  
  2. typedef struct { unsigned long pmd; } pmd_t;  
  3. typedef struct { unsigned long pgd; } pgd_t;  
  4. typedef struct { unsigned long pgprot; } pgprot_t;  //操作標志  


3.2用戶空間的映射:

1. 用戶空間的虛擬地址vaddr通過MMU(pgd,pmd,pte)找到對應的頁表項x(即為物理地址)
2. 頁表項x的高20位是物理也好,物理頁號index = x >> PAGE_SHIFT, 同理,index后面補上12個0就是物理頁表的首地址。
3. 通過物理頁號,我們可以再內核中找到該物理頁的描述的指針mem_map[index],當然這個指針是虛擬地址,page結構見上文。


3.3內核空間虛擬地址的映射:

內核空間與物理地址之間有直接的映射關系,而不需要向用戶空間那樣通過mmu(pgd)。系統空間映射(3G開始)到物理空間0G起始:
例如:
系統內核映像載入的虛擬地址為3G+1M的起始地址,那么對應的物理地址為1M。
緊接着分配在3G+2M開始分配了8M的虛擬地址(物理地址為2-9M)用於PDG
之后預留了16M空間用DMA於存儲。
而全局的page結構的mem_page[]數組是在0xc1000000開始的。
所以內核空間虛擬地址到物理地址的轉換為:

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. PAGE_OFFSET = 3GB  
  2. vitr_to_phys(kadd)  
  3.   return vadd - PAGE_OFFSET  
  4.     
  5. 內核空間的虛擬地址vaddr是通過如下方式找到它對應物理地址的page結構:  
  6. vitr_to_page(vadd)  
  7.     index = virt_to_phys(kadd) >> PAGE_SHIFT  
  8.     return mem_map[index]  

4. 相關數據結構關系圖

說明:

1. 黑色+紅色 箭頭展示了虛擬地址空間到物理空間的映射關系

2. 藍色箭頭涉涉及到文件的映射操作mmap(),相比匿名映射,文件映射多了文件層的磁盤IO。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM