關(guān)注”技術(shù)簡說“(b站同名),帶你由淺入深學習linux內(nèi)核源碼。linux內(nèi)核開發(fā)100講免費教程,每天晚上9點準時更新,敬請收看。進我主頁點”視頻“欄目即可觀看。
很多講linux內(nèi)核的書里面都提到過這四個概念:邏輯地址、虛擬地址、線性地址和物理地址。物理地址比較好澄清,但是這些書里都沒有明確的講清楚所謂的邏輯地址、虛擬地址、線性地址的區(qū)別到底是什么?
那本文就來個大掃除吧。
中英文對應(yīng)關(guān)系:
邏輯地址 --- logical address;
虛擬地址 --- virtual address;
線性地址 --- linear address;
物理地址 --- physical address;
這四個地址是體系相關(guān)的,我以x86 cpu為例進行解釋。
一、名詞解釋
先放張圖吧
x86 cpu 段頁式內(nèi)存管理機制
1.左上角的Logical Address,就是我們所說的邏輯地址。
邏輯地址,是由一個段選擇符加上一個指定段內(nèi)相對地址的偏移量(Offset)組成的,表示為 [段選擇符:段內(nèi)偏移量],例如:[CS:EIP]
2.虛擬地址,其實就是如上邏輯地址的段內(nèi)偏移Offset。所以:
邏輯地址可以表示為 [段標識符:虛擬地址]
驅(qū)動代碼或者應(yīng)用程序中所用的地址就是虛擬地址,比如以下程序中指針p的輸出:
#include #include void main(void){ int *p; p = (int*)malloc(sizeof(int)); printf('addres is %p\n', p); free(p); return;}
3.Linear Address, 也就是我們所說的線性地址
線性地址是平坦的統(tǒng)一地址空間。
intel x86 中,線性地址是由邏輯地址經(jīng)過段頁式轉(zhuǎn)換得到的。
4.最右邊的Physical Address, 也就是我們所說的物理地址。
物理地址就是物理內(nèi)存的地址。但是注意在做頁表轉(zhuǎn)換的時候,這里存的可不是真正的物理地址,而是物理內(nèi)存塊的編號。
內(nèi)核把物理內(nèi)存按照4K大小編號,考慮k到物理內(nèi)存的起始地址是固定的,所以從內(nèi)存編號的序號就可以算出該編號對應(yīng)的物理內(nèi)存塊的起始地址了。例如:
物理內(nèi)存起始地址為0x50000000, 那么編號為0的物理塊的起始地址為:0x50000000
編號為1的物理塊的起始地址為 0x50001000
以此類推。。。
所以,根據(jù)物理內(nèi)存塊的編號,就可以轉(zhuǎn)換得到該物理內(nèi)存塊的起始地址,也叫做物理內(nèi)存的基地址。了解這一點非常重要,因為后續(xù)做頁表映射的時候會用到。
二、x86的段頁式內(nèi)存管理機制
還是上面的那張圖,除了表達了這三個地址之外,我們還可以看出從邏輯地址轉(zhuǎn)換成最終的物理地址,要經(jīng)歷兩個過程:
1.邏輯地址轉(zhuǎn)換為線性地址
在 Intel 平臺下,邏輯地址(logical address)是 selector:offset 這種形式,selector 可以是代碼段或者數(shù)據(jù)段,offset 是段內(nèi)偏移。如果用 selector 去 GDT( 全局描述符表 ) 里拿到 segment base address(段基址) 然后加上 offset(段內(nèi)偏移),這就得到了 linear address。我們把這個過程稱作段式內(nèi)存管理。
總結(jié)來看:邏輯地址轉(zhuǎn)換為線性地址的詳細過程是這樣的:
(1)先從段選擇符(selector)中得到段描述符;
(2)從段描述符中得到段基地址;
(3)線性地址=上一步得到的段基地址+段內(nèi)偏移(也就是上文說的虛擬地址)
我們來看看端選擇符(selector)的組成:
13位的索引號對應(yīng)到段描述符表中的位置,而T1字段表示使用的是哪個段描述符表。
Intel設(shè)計的本意是,一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每個進程自己的,就放在所謂的“局部段描述符表(LDT)”中。所以,通過T1字段就可以選擇是使用
GDT,還是使用LDT了。GDT或者LDT里的內(nèi)容就是一個一個的段描述符,段描述符的組成如下:
這些東東很復雜,不過,我這里只關(guān)心一樣,就是Base字段,它描述了一個段的的基地址。
得到了這個基地址,然后再加上段內(nèi)偏移offset,就得到了最終的線性地址。
2.線性地址轉(zhuǎn)換為物理地址
如果再把線性地址切成三段,用前兩段分別作為索引去PGD、Page Table里查表,會先得到一個頁目錄表項、然后會得到一個頁表項(Page Table Entry),那里面的值就是一個物理內(nèi)存塊的起始地址(其實就是是物理內(nèi)存編號),把它加上 linear address 切分之后第三段的內(nèi)容(又叫頁內(nèi)偏移)就得到了最終的 physical address。我們把這個過程稱作頁式內(nèi)存管理。
所以,x86采用的是段頁式內(nèi)存管理的方式。
三、linux內(nèi)核中邏輯地址、虛擬地址和線性地址的關(guān)系
上一部分提到了x86采用了段頁式內(nèi)存管理。
按照 Intel 的設(shè)計,段式內(nèi)存管理中的段類型分為三種:代碼段、數(shù)據(jù)段、系統(tǒng)段(TSS之類的),實在是太麻煩了。我們只靠頁式內(nèi)存管理就已經(jīng)可以完成Linux內(nèi)核需要的所有功能,根本不需要段映射,但是段映射這玩意兒又關(guān)不掉,那就只能上點手段了。
于是,Linux內(nèi)核將所有類型的段的 segment base address 都設(shè)成0(包括內(nèi)核數(shù)據(jù)段、內(nèi)核代碼段、用戶數(shù)據(jù)段、用戶代碼段等)。那么這樣一來所有段都重合了,也就是不分段了,此外由于段限長是地址總線的尋址限度,所以這也就相當于所有段內(nèi)空間跟整個線性空間重合了。
這樣邏輯地址也就簡化為了段內(nèi)的偏移量(邏輯地址=虛擬地址)。
由于段基地址變?yōu)榱?,那么線性地址=邏輯地址=虛擬地址。
所以,在x86 linux內(nèi)核里,邏輯地址、虛擬地址、線性地址,這是三個地址是一致的。
親愛的小伙伴們,你們搞清楚了嗎?
關(guān)注”技術(shù)簡說“(b站同名),帶你由淺入深學習linux內(nèi)核源碼。linux內(nèi)核開發(fā)100講免費教程,每天晚上9點準時更新,敬請收看。進我主頁點”視頻“欄目即可觀看。