本文裝載自:http://www.cnblogs.com/xuqiang/archive/2010/03/29/1953689.html
這裡開始分析hello中的一個定址過程的實現。當然現在的情景是:(當然可能只是一小部分載入到了記憶體中,大部分的需要使用缺頁異常處理來實現記憶體配置)。
在sys_exece()函數中,在記憶體ram中儲存了命令列參數,環境參數,但是程式碼片段,資料區段,bss段,可執行檔的其他段提供”映射“(映射的具體含義參見"深入理解Hello World 3"),此時檔案到虛存的映射僅僅是建立了一種映射關係,也就是說,虛存頁面到物理頁面之間的映射還沒有建立。
在說明記憶體定址 之前,先來看看進程是如何管理虛擬位址,然後開始說明在沒有缺頁的情況下,保護模式下的定址,最後開始hello的情景分析,當hello程式開始定址記憶體的某個位置時,到底發生了什麼(還是從hello剛被載入到記憶體中)?
1.進程的地址空間及管理
該部分參見的是《深入理解linux核心。
核心中的函數通過相當直接了當的方式來得到動態記憶體。核心對於記憶體的分配時採用不同的方式,對於核心而言,總是立即分配記憶體,但是對於使用者進程而言,核心總是在“不能再延遲的情況下”才分配記憶體。linux下的進程總是維護自己的虛擬位址空間(linux為每個使用者進程 分配4DB虛擬位址空間),而虛擬記憶體到實體記憶體的實際轉換的話,是採用的"頁表“的機制來實現的。下面說明相關的資料結構:
在核心源碼include/linux/sched.h中定義了stask_struct結構,用來標記每個進程:
struct task_struct {
...
struct mm_struct *mm, *active_mm;
...
}
進程的task_struct中的mm指向mm_struct用來維護進程管理的虛擬記憶體,哪些虛擬記憶體地址使用,而那些虛擬記憶體地址還沒有使用,mm_struct定義如下:
structmm_struct
{
unsignedlong
start_code,
end_code,
end_data;
...
struct rb_toor mm_root;
structvm_area_struct *
mmap;
structvm_area_struct *
mmap_avl;
..
}
所有的mm_struct都是存放在一個鏈表中,鏈表的第一個元素是init_mm元素,init_mm是初始化進程0使用的記憶體描述符。定義:
#define INIT_MM { \
0, \
0, 0, 0, \ 0, 0, 0, 0, \ 0, 0, 0, 0, \ 0, \ */ 0, 0, 0, 0, \ 0, \ */ 0, 0, 0, 0, \ &init_mmap, &init_mmap
}
vm_area_struct定義如下:
/* * 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 */ pgprot_t vm_page_prot; unsigned short vm_flags; /* AVL tree of VM areas per task, sorted by address */... /* For areas with inode, the list inode->i_mmap{,_shared}, for shm areas, * the list of attaches, otherwise unused. */... struct vm_operations_struct * vm_ops; unsigned long vm_offset; struct file * vm_file; unsigned long vm_pte; /* shared mem */};上面的vm_area_struct 中需要特彆強調的是vm_file,vm_ops,vm_mm,vm_mm指向進程的mm欄位, vm_file執行虛擬位址映射的檔案的
file對象,vm_ops欄位指向vm_operations_struct結構,該結構中 存放的是範圍線性區的方法,常見的有:open,close,nopage,
populate。
總結上面可得:
process --> task_struct --> mm(mm_struct) --> vm_area_struct list來管理進程虛擬位址空間
瞭解了上面的資料結構之後,開始看進程的虛擬位址空間是如何建立的?如何刪除的?在這裡hello的情景中,進程是由sys_execve()系統調用來
建立的,在sys_execve函數中,首先是分配一個頁框(物理地址)填充進程的命令列參數,環境變數,將這個頁框分配各這個進程(設定頁表),
然後開始映射可執行檔的.text段,這裡”映射text段“所做的工作只是分配虛擬位址,將可執行檔的一部分和它對應上,在實際的實體記憶體中是
不存在該“段”的內容,如果需要訪問的話 ,產生"缺頁異常",調用異常處理函數將可執行檔對應的部分讀入到記憶體中,並設定相關的頁表(這一步
其實才建立了虛擬位址和物理地址的映射,也就是說映射其實分為兩個部分,虛擬位址 <--> 檔案 虛擬位址 <-->物理地址,具體的映射部分參見
第四部分)
刪除進程的地址空間使用的是exit_mm。具體的參見“深入理解Hello World程式卸載”部分。
2.保護模式下定址
電腦中引入了”虛擬記憶體“的東西,好處是很大,但是還是給程式的理解帶還很多的麻煩啊。我學習時主要疑問是:
1.哪些是虛擬位址?哪些是實際的物理地址?
如果開啟了分頁機制的話,在應用程式中使用的所有地址都是虛擬位址,將虛擬位址轉換成物理地址的工作是有硬體來完成,同時使用軟體來初始化
一些table,顯然必須存在一些”東西“來記錄實際的物理地址,這個工作是寄存器中寫入物理地址的值。
2.linux為每個活動的進程分配i、一個頁目錄項。
至於具體的轉換過程,在很多作業系統課本中講的有。
3.hello情景分析
依舊使用上面的假設:hello程式剛剛被載入到記憶體中,下面需要定址一個text段內容,由於在sys_execve中做的僅是映射,那麼虛擬位址在轉換
成物理地址時,所訪問的頁表項是空,產生缺頁異常,調用缺頁異常處理函數,讀入該塊美容,然後讀取頁表,cpu的地指線上就是該物理地址的值。然後得到該物理地址中的值,最終開始執行這條指令
下一步開始解決的是“映射”的實際含義?這其中設計到了檔案系統的相關東西,好像雪球越來越大了,繼續。。。
哎,順便說上一句,核心龐大的代碼,尋找一個函數或者是資料結構的定義是在是很難,推薦使用的是google code search來完成相關搜尋。