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 虛擬空間、使用者空間
以上結構描述了程序的使用者空間的結構,其中struct mm_struct { struct vm_area_struct * mmap; /* list of VMAs */ ... pgd_t * pgd; //用於地址對映 atomic_t mm_users; /* How many users with user space? */ atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */ int map_count; /* number of VMAs */ ... //描述使用者空間的段分佈:資料段,程式碼段,堆疊段 unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end; unsigned long rss, total_vm, locked_vm; ... };
pgd_t 是該程序使用者空間地址對映到實體地址時使用
vm_area_struct 是程序使用者空間已對映到物理空間的虛擬地址區間,mmap是該空間區塊組成的連結串列。
虛擬空間的空洞:虛擬空間還未被對映的區塊(即沒有被使用),那麼就沒有vm_area_struct結構
2.2 記憶體區間
/* * This struct defines a memory VMM memory area. There is one of these * per VM-area/task. A VM area is any part of the process virtual memory * space that has a special rule for the page-fault handlers (ie a shared * library, the executable area etc). */ struct vm_area_struct { struct mm_struct * vm_mm; /* VM area parameters */ unsigned long vm_start; //虛擬空間起始地址 unsigned long vm_end; //終止地址 /* linked list of VM areas per task, sorted by address */ struct vm_area_struct *vm_next; //該區間的許可權及標誌 pgprot_t vm_page_prot; unsigned long vm_flags; //一些vm_area 的連結 ... struct vm_operations_struct * vm_ops; unsigned long vm_pgoff; /* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */ struct file * vm_file; //用於將磁碟檔案對映至使用者空間 ... };
虛擬空間區間的描述中:
vm_start/vm_end 為該區塊的起始和結束地址
vm_file 是在檔案對映中使用到,即常用的mmap(fd,...)函式,簡單說即將虛擬空間對映至檔案在核心的緩衝區,那麼這時候訪問該虛擬空間將有別於pgd的對映。
vm_operations_struct 為本虛擬區間的操作,其中的nopage函式指標是處理記憶體缺頁而使用的。對於通用的記憶體對映,該缺頁處理函式為do_no_page()將虛擬地址對映到實體地址(匿名對映):分配物理頁& 設定pgd & pte。
而對於mmap操作相關的虛擬地址,其缺頁處理函式將和檔案系統的缺頁函式相關,filemap_nopage(),通過檔案系統的缺頁從磁碟將相關檔案塊載入如核心緩衝區.
struct vm_operations_struct { void (*open)(struct vm_area_struct * area); void (*close)(struct vm_area_struct * area); struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access); //缺頁操作 };
3.系統實體地址的組織
核心將實體地址按頁來組織,struct page描述系統的物理頁的資訊,但是頁的資料內容是不在該結構中的。系統有全域性資料 struct page mem_map[],用於記錄每個物理頁。
頁面大小為4kb,在原始碼中用體現為(PAGE_SHIFT = 12)
/*
* Try to keep the most commonly accessed fields in single cache lines
* here (16 bytes or greater). This ordering should be particularly
* beneficial on 32-bit processors.
*
* The first line is data used in page cache lookup, the second line
* is used for linear searches (eg. clock algorithm scans).
*/
typedef struct page {
struct list_head list;
struct address_space *mapping;
unsigned long index;
struct page *next_hash;
atomic_t count;
unsigned long flags; /* atomic flags, some possibly updated asynchronously */
struct list_head lru;
unsigned long age;
wait_queue_head_t wait;
struct page **pprev_hash;
struct buffer_head * buffers;
void *virtual; /* non-NULL if kmapped */
struct zone_struct *zone;
} 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中存放的即為對應物理頁面的指標。
typedef struct { unsigned long pte; } pte_t;
typedef struct { unsigned long pmd; } pmd_t;
typedef struct { unsigned long pgd; } pgd_t;
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開始的。
所以核心空間虛擬地址到實體地址的轉換為:
PAGE_OFFSET = 3GB
vitr_to_phys(kadd)
return vadd - PAGE_OFFSET
核心空間的虛擬地址vaddr是通過如下方式找到它對應實體地址的page結構:
vitr_to_page(vadd)
index = virt_to_phys(kadd) >> PAGE_SHIFT
return mem_map[index]
4. 相關資料結構關係圖
說明:
1. 黑色+紅色 箭頭展示了虛擬地址空間到物理空間的對映關係
2. 藍色箭頭涉涉及到檔案的對映操作mmap(),相比匿名對映,檔案對映多了檔案層的磁碟IO。