乙個實際的段、頁式記憶體管理
mmu位址處理
參考資料
首先兩個條件,第一:物理記憶體必須得是分頁管理的;第二:對使用者來說是分段的。但是使用者程式最終又得在記憶體上面跑,因此肯定需要某種機制或者轉化使得以使用者程式的視角看起來記憶體是分段的,以物理記憶體的視角看起來又是分頁的,這種機制就是虛擬記憶體
虛擬記憶體是一種和物理記憶體差不多的東西,每乙個位元組都有對應的位址。但是有一點與物理記憶體不同,其實從它的名字就能看出來,「虛擬」:即實際上並不存在,它只是一種機制,純粹是用程式表示的,沒有這種硬體。它的作用就是讓上層程式看起來是記憶體是分段的,而實際上是分頁的。那它是怎麼實現的呢?
基本思想:使用者程式使用了一段記憶體,那麼首先會在虛擬記憶體上面找到一段空的記憶體,然後將使用者程式使用的記憶體對映到這段記憶體上,然後虛擬記憶體再將這段記憶體對映到物理記憶體上。
從這裡能看出,使用者程式使用的邏輯記憶體經過了兩次對映才達到物理記憶體,第一次對映是段的對映,需要段表;第二次是頁的對映,需要頁表。那麼邏輯位址究竟是如何變成實體地址的呢?邏輯位址是段號+偏移(cs:ip)組成的,首先根據段號在段表中找到虛擬記憶體的段基址,然後加上偏移得到虛擬位址(即在虛擬記憶體上面的位址),格式是:頁號+偏移。然後根據頁號在頁表中找到對應的頁框號,再加上偏移得到最後的實體地址。實現了邏輯位址與實體地址的對應。也就是重定位操作。
記憶體管理的核心就是記憶體分配,所以從程式放入記憶體、使用記憶體開始。其實只要程式可以正常執行,就說明記憶體使用起來了;管理就是如何更好的使用;這點可以類似cpu的管理,首先使用起來,然後如何讓它更高效的使用。
程式放入記憶體首先就是要在虛擬記憶體中給它分配段、建立段表,然後是分配頁、建立頁表;注意先後關係。第一步:在虛擬記憶體上分配段;如何分配呢?首先肯定是得找到空閒的段,如何找,可以使用前面談到過的記憶體分割槽方法。這個見前文《記憶體的分段與分頁》。然後將使用者程式對映到虛擬記憶體,建立段表,然後分配頁,建立頁表。這裡的分配頁並不是在記憶體裡面查詢空閒頁的,而是另外一種方式。
建立程序使用的是fork()系統呼叫,從前面系統呼叫的知識可以知道fork()呼叫首先是sys_fork->copy_process。
在linux/kernel/fork.c
int copy_process(int nr, long ebp...)
int copy_mem(int nr, task_struct *p)
上面是fork()建立乙個程序執行的**。進入copy_process後,在copy_process中呼叫copy_men();這個函式就是給該程序在虛擬記憶體上分配記憶體空間的,形參nr和p分別表示:第nr個程序和該程序的pcb。ldt表示的是段表。
new_data_base = nr*0x4000000; // nr * 64m
首先給該程序在虛擬記憶體上分配一塊64m的記憶體塊。可以看到第0個程序記憶體區域就是0 ~ 64m,第乙個程序64~128m,依次類推,互不重疊。然後將p的ldt[1]和ldt[2]都指向這塊記憶體。如下圖
這裡的ldt[1]和ldt[2]指的是資料段和**段,這裡的資料段和**段
到這裡為止,在虛擬記憶體上分配記憶體、建立段表就弄好了。
接下來就是分配記憶體、建立頁表。還是上面那個copy_mem()函式
int copy_mem(int nr, task_struct *p)
int copy_page_tables(unsigned long from, unsigned long to , long size)
}
首先看copy_mem函式
old_data_base = get_base(currnet->ldt[2]);
這條語句的含義就是得到當前程序的虛擬記憶體位址賦給old_data_base;然後呼叫copy_page_tables()函式,首先from和to是什麼?從形參以及copy_mem裡面的呼叫可以看出,這兩個都是32為虛擬記憶體位址。from_dir指向乙個父程序的頁目錄項(章),to_dir指向乙個子程序的頁目錄項(章)。前面說過了32位虛擬記憶體位址的構成如下圖。
from_dir = (unsigned long *) ((from>>20) & 0xffc);
這句話是什麼意思?from右移22位得到的是頁目錄號,但是
(from>>20) & 0xffc
size就是頁目錄項數(章數)。
for (; size-->0; from_dir++, to_dir++)
前面說過from_dir指向乙個父程序的頁目錄項(章),那麼*from_dir就是from_dir對應的那個頁目錄表(節),也就是from_page_table的含義。get_free_page()新建乙個子程序的頁目錄表(節);然後將這個頁目錄表賦給to_dir,但是to_dir指向的這個表裡面的內容還是空的,接下來就是要將這個表填上
for (; nr-->0; from_page_table++, to_page_table++)
主要就是看這一段
this_page = *from_page_table;
this_page &= ~2; // 設定為唯讀
*to_page_table = this_page;
這三句的含義就是將父程序的from_page_table賦值給子程序的to_page_table,並且將對應的頁設定為唯讀。這也是前面說的為什麼不用為子程序找空閒頁,因為子程序用的就是父程序的記憶體。為什麼要設定為唯讀屬性?兩個程序共享同一塊記憶體,如果都是讀,沒有任何問題,但是如何要寫呢?那麼就出問題了;因此要設定為唯讀。到這裡分配物理頁、建立頁表就說完了。
到目前為止,分配記憶體、建立段表,分配記憶體、建立頁表都講完了。程式就可以正確的儲存到物理記憶體了。真不容易啊-。接下來程式執行的時候只需要根據這兩張表找到對應的記憶體就好了;當然如果查這兩張表的操作全部由軟體來實現的話就要浪費很多時間了,因此計算機將查表的操作交給硬體來完成,只要從使用者程式那裡得到cs:ip,硬體會自動得到該邏輯位址對應的實體地址的,這個硬體就是mmu。
哈工大李志軍作業系統
作業系統(十) 段頁結合的實際記憶體管理模型
乙個實際的段 頁式記憶體管理 mmu位址處理 參考資料 首先兩個條件,第一 物理記憶體必須得是分頁管理的 第二 對使用者來說是分段的。但是使用者程式最終又得在記憶體上面跑,因此肯定需要某種機制或者轉化使得以使用者程式的視角看起來記憶體是分段的,以物理記憶體的視角看起來又是分頁的,這種機制就是虛擬記憶...
作業系統 段頁結合的實際記憶體管理
段 頁結合 程式設計師希望用段,物理記憶體希望用頁,所以 從虛擬記憶體中割出乙個區域來給這個段,段就有了對映,但是這個虛擬記憶體不能正真的使用,需要把虛擬記憶體再對映一次,這個時候虛擬記憶體中的段分割成固定大小的頁,每一頁再去對映到物理記憶體中 段面向使用者,頁面向硬體 也就是位址翻譯 從使用者的角...
段頁結合的實際記憶體管理 虛擬記憶體 OS
如何讓作業系統既支援段又支援頁?虛擬記憶體 我們讓應用程式分為段,然後對映到一段虛擬記憶體中,再讓虛擬記憶體對映到物理記憶體中的頁中,這樣就完成了段和頁的結合 段 頁同時存在時的重定位 位址翻譯 乙個實際的段 頁式記憶體管理 記憶體管理核心 記憶體分配 使用記憶體分為五步 1.分配段 建段表 for...