背景 : 在此文章裏會從分頁分段機制去解析Linux內存管理系統如何工做的,因爲Linux內存管理過於複雜而本人能力有限。會盡可能將本身總結概括的部分寫清晰。數組
從實模式到保護模式的尋址方式的不一樣 : spa
16位CPU的尋址方式 : 在 8086 CPU 中,提供了兩類寄存器來進行尋址,分別爲段寄存器(例如 CS,DS,SS)和段偏移寄存器(例如 SI,DI,SP)。而這幾種寄存器的長度都爲16bit,尋址方式也很簡單 : cs:ip = (cs << 4 + ip)。也就是說 cs寄存器的值左移4位加上ip的值獲得的就是物理地址(物理地址就是內存中真實的值)。
3d
32位CPU的尋址方式 : 在80X86 CPU 中,提供了分段與分頁機制。對於CPU的尋址而言,再也不像 8086 CPU 那般將 段寄存器段偏移寄存器 直接運算獲得結果。指針
1)那麼在32位CPU中是如何尋址的呢?blog
i)如何開啓分頁分段模式?索引
首先要介紹的就是 CR0 寄存器(以下圖):ip
對於CR0來講,存在兩個bit : 內存
PE位 : 如若置位(1)則表示開啓保護(分段)模式。it
PG位 : 在PE位置位的前提下置位PG位表示開啓分頁模式。內存管理
ii)分段機制如何進行尋址(獲得線性地址)?
簡述 : 段寄存器(例如CS) 裏面存在一個索引(index),它會根據GDTR寄存器找到一個表(GDT),而後這個表裏面有元素,元素內部含有段基址。而這個段基址加上段變址寄存器的值就直接獲得了線性地址的值。
詳述 :
在開啓分段模式以後,段寄存器裏面的值的含義就再也不只是一個簡單段基址了(也就是 (cs << 4)獲得段基址),當下段寄存器加載的值稱爲段選擇子,結構以下:
能夠看到這裏有一個由幾個bit組成的 描述符索引(也就是簡述裏所說的index),以及TI和RPL位(但目前不用管它)。
GDTR :
能夠看到GDTR和IDTR(這個實際上是另外一個相似於GDTR的寄存器)都是由線性基地址和表長度組成,線性基地址也就是說這個表的頭部所在的線性基地址(相似於數組名),表長度也就是這個表的長度啦。
那麼天然咱們就能獲得一個相似於數組(由連續的地址組成)的表。
對於這個"數組"來講,它的元素則被稱爲 段描述符:
能夠看到段描述符很長(一共64bit)...可是不要緊,咱們當下只須要把其分爲三個部分 : 段基地址,段限長,段屬性。便可。(這裏之因此基地址和段限長啥的分了幾個部分主要是由於歷史遺留問題,可是不要緊,他們只不過須要把幾個分開的連在一塊兒就能獲得了真正的段基地址了)。
那麼獲得了段基址,咱們天然將其與段變址寄存器內的值相加就獲得了線性地址了!
iii)分頁機制如何進行尋址(獲得物理地址)?
如若咱們開始分頁了,那麼就表示咱們已經獲得了一個線性地址(分頁是在分段的基礎上進行的)。
簡述 : 首先咱們把線性地址分爲幾個部分,目錄(本質是頁目錄表的索引),頁面(本質是頁表的索引),頁內偏移(本質是偏移量)。
由 CR3寄存器 做爲 頁目錄表 的指針,經過CR3寄存器就能夠獲得一個表稱爲頁目錄表,頁目錄表內元素 稱爲 頁目錄項, 頁目錄項本質也是一個指針,指向一個 頁表, 而頁表內元素稱爲頁表項,頁表項內存在着 頁基地址, 物理地址 = 頁基地址(物理基地址) + 頁內偏移(物理偏移地址)。
簡單來講咱們能夠把 頁目錄表和頁表想象成一個二維的數組。頁目錄表元素是頁表(一維數組),頁表元素則是頁基地址。
咱們只須要有兩個元素(頁目錄表索引和頁表索引)就能夠獲得一個物理(頁)基地址,而後咱們再將 頁內偏移加上物理基地址,就獲得了真正的物理地址了!而一個頁在80x86中是4K大小(頁基址 至 頁基址 + 4K 爲一頁)。因此內存管理的頁也是4K大小。
附圖(寄存器數據) :
由圖我門能夠知道,頁基地址(頁幀),是4K對齊的(2^12 = 4K),也就是說頁表項內只有12 - 31位是頁基地址,其餘的位是頁屬性,每次經過頁表項計算物理地址只須要將 0 - 11位復位(0),便可。
對於頁屬性 : 表述這個頁的權限之類的,由於有的頁面是屬於內核才能去使用的。更重要的一點是 : 這個頁是否存在。
頁目錄和頁表的表項格式:
如圖所示 : 咱們能夠知道當 P位 被置位則表示頁面存在,當 P位復位(爲0) 則表示頁面不存在,如若頁面不存在,那麼就會產生缺頁中斷,執行缺頁中斷處理程序。