亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

Chinaunix

標(biāo)題: 我理解的邏輯地址、線性地址、物理地址和虛擬地址(補充完整了) [打印本頁]

作者: 獨孤九賤    時間: 2008-01-15 16:32
標(biāo)題: 我理解的邏輯地址、線性地址、物理地址和虛擬地址(補充完整了)
要過年了,發(fā)個年終總結(jié)貼,只是個人理解,不包正確哈。

本貼涉及的硬件平臺是X86,如果是其它平臺,嘻嘻,不保證能一一對號入座,但是舉一反三,我想是完全可行的。

一、概念

物理地址(physical address)
用于內(nèi)存芯片級的單元尋址,與處理器和CPU連接的地址總線相對應(yīng)。
——這個概念應(yīng)該是這幾個概念中最好理解的一個,但是值得一提的是,雖然可以直接把物理地址理解成插在機器上那根內(nèi)存本身,把內(nèi)存看成一個從0字節(jié)一直到最大空量逐字節(jié)的編號的大數(shù)組,然后把這個數(shù)組叫做物理地址,但是事實上,這只是一個硬件提供給軟件的抽像,內(nèi)存的尋址方式并不是這樣。所以,說它是“與地址總線相對應(yīng)”,是更貼切一些,不過拋開對物理內(nèi)存尋址方式的考慮,直接把物理地址與物理的內(nèi)存一一對應(yīng),也是可以接受的。也許錯誤的理解更利于形而上的抽像。

虛擬內(nèi)存(virtual memory)
這是對整個內(nèi)存(不要與機器上插那條對上號)的抽像描述。它是相對于物理內(nèi)存來講的,可以直接理解成“不直實的”,“假的”內(nèi)存,例如,一個0x08000000內(nèi)存地址,它并不對就物理地址上那個大數(shù)組中0x08000000 - 1那個地址元素;
之所以是這樣,是因為現(xiàn)代操作系統(tǒng)都提供了一種內(nèi)存管理的抽像,即虛擬內(nèi)存(virtual memory)。進程使用虛擬內(nèi)存中的地址,由操作系統(tǒng)協(xié)助相關(guān)硬件,把它“轉(zhuǎn)換”成真正的物理地址。這個“轉(zhuǎn)換”,是所有問題討論的關(guān)鍵。
有了這樣的抽像,一個程序,就可以使用比真實物理地址大得多的地址空間。(拆東墻,補西墻,銀行也是這樣子做的),甚至多個進程可以使用相同的地址。不奇怪,因為轉(zhuǎn)換后的物理地址并非相同的。
——可以把連接后的程序反編譯看一下,發(fā)現(xiàn)連接器已經(jīng)為程序分配了一個地址,例如,要調(diào)用某個函數(shù)A,代碼不是call A,而是call 0x0811111111 ,也就是說,函數(shù)A的地址已經(jīng)被定下來了。沒有這樣的“轉(zhuǎn)換”,沒有虛擬地址的概念,這樣做是根本行不通的。
打住了,這個問題再說下去,就收不住了。

邏輯地址(logical address)
Intel為了兼容,將遠古時代的段式內(nèi)存管理方式保留了下來。邏輯地址指的是機器語言指令中,用來指定一個操作數(shù)或者是一條指令的地址。以上例,我們說的連接器為A分配的0x08111111這個地址就是邏輯地址。
——不過不好意思,這樣說,好像又違背了Intel中段式管理中,對邏輯地址要求,“一個邏輯地址,是由一個段標(biāo)識符加上一個指定段內(nèi)相對地址的偏移量,表示為 [段標(biāo)識符:段內(nèi)偏移量],也就是說,上例中那個0x08111111,應(yīng)該表示為[A的代碼段標(biāo)識符: 0x08111111],這樣,才完整一些”

線性地址(linear address)或也叫虛擬地址(virtual address)
跟邏輯地址類似,它也是一個不真實的地址,如果邏輯地址是對應(yīng)的硬件平臺段式管理轉(zhuǎn)換前地址的話,那么線性地址則對應(yīng)了硬件頁式內(nèi)存的轉(zhuǎn)換前地址。

-------------------------------------------------------------
CPU將一個虛擬內(nèi)存空間中的地址轉(zhuǎn)換為物理地址,需要進行兩步:首先將給定一個邏輯地址(其實是段內(nèi)偏移量,這個一定要理解。。。,CPU要利用其段式內(nèi)存管理單元,先將為個邏輯地址轉(zhuǎn)換成一個線程地址,再利用其頁式內(nèi)存管理單元,轉(zhuǎn)換為最終物理地址。

這樣做兩次轉(zhuǎn)換,的確是非常麻煩而且沒有必要的,因為直接可以把線性地址抽像給進程。之所以這樣冗余,Intel完全是為了兼容而已。

2、CPU段式內(nèi)存管理,邏輯地址如何轉(zhuǎn)換為線性地址
一個邏輯地址由兩部份組成,段標(biāo)識符: 段內(nèi)偏移量。段標(biāo)識符是由一個16位長的字段組成,稱為段選擇符。其中前13位是一個索引號。后面3位包含一些硬件細節(jié),如圖:

最后兩位涉及權(quán)限檢查,本貼中不包含。

索引號,或者直接理解成數(shù)組下標(biāo)——那它總要對應(yīng)一個數(shù)組吧,它又是什么東東的索引呢?這個東東就是“段描述符(segment descriptor)”,呵呵,段描述符具體地址描述了一個段(對于“段”這個字眼的理解,我是把它想像成,拿了一把刀,把虛擬內(nèi)存,砍成若干的截——段)。這樣,很多個段描述符,就組了一個數(shù)組,叫“段描述符表”,這樣,可以通過段標(biāo)識符的前13位,直接在段描述符表中找到一個具體的段描述符,這個描述符就描述了一個段,我剛才對段的抽像不太準(zhǔn)確,因為看看描述符里面究竟有什么東東——也就是它究竟是如何描述的,就理解段究竟有什么東東了,每一個段描述符由8個字節(jié)組成,如下圖:

這些東東很復(fù)雜,雖然可以利用一個數(shù)據(jù)結(jié)構(gòu)來定義它,不過,我這里只關(guān)心一樣,就是Base字段,它描述了一個段的開始位置的線性地址。

Intel設(shè)計的本意是,一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每個進程自己的,就放在所謂的“局部段描述符表(LDT)”中。那究竟什么時候該用GDT,什么時候該用LDT呢?這是由段選擇符中的T1字段表示的,=0,表示用GDT,=1表示用LDT。

GDT在內(nèi)存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT則在ldtr寄存器中。

好多概念,像繞口令一樣。這張圖看起來要直觀些:

首先,給定一個完整的邏輯地址[段選擇符:段內(nèi)偏移地址],
1、看段選擇符的T1=0還是1,知道當(dāng)前要轉(zhuǎn)換是GDT中的段,還是LDT中的段,再根據(jù)相應(yīng)寄存器,得到其地址和大小。我們就有了一個數(shù)組了。
2、拿出段選擇符中前13位,可以在這個數(shù)組中,查找到對應(yīng)的段描述符,這樣,它了Base,即基地址就知道了。
3、把Base + offset,就是要轉(zhuǎn)換的線性地址了。

還是挺簡單的,對于軟件來講,原則上就需要把硬件轉(zhuǎn)換所需的信息準(zhǔn)備好,就可以讓硬件來完成這個轉(zhuǎn)換了。OK,來看看Linux怎么做的。

3、Linux的段式管理
Intel要求兩次轉(zhuǎn)換,這樣雖說是兼容了,但是卻是很冗余,呵呵,沒辦法,硬件要求這樣做了,軟件就只能照辦,怎么著也得形式主義一樣。
另一方面,其它某些硬件平臺,沒有二次轉(zhuǎn)換的概念,Linux也需要提供一個高層抽像,來提供一個統(tǒng)一的界面。所以,Linux的段式管理,事實上只是“哄騙”了一下硬件而已。

按照Intel的本意,全局的用GDT,每個進程自己的用LDT——不過Linux則對所有的進程都使用了相同的段來對指令和數(shù)據(jù)尋址。即用戶數(shù)據(jù)段,用戶代碼段,對應(yīng)的,內(nèi)核中的是內(nèi)核數(shù)據(jù)段和內(nèi)核代碼段。這樣做沒有什么奇怪的,本來就是走形式嘛,像我們寫年終總結(jié)一樣。
include/asm-i386/segment.h
  1. #define GDT_ENTRY_DEFAULT_USER_CS        14
  2. #define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS * 8 + 3)

  3. #define GDT_ENTRY_DEFAULT_USER_DS        15
  4. #define __USER_DS (GDT_ENTRY_DEFAULT_USER_DS * 8 + 3)

  5. #define GDT_ENTRY_KERNEL_BASE        12

  6. #define GDT_ENTRY_KERNEL_CS                (GDT_ENTRY_KERNEL_BASE + 0)
  7. #define __KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8)

  8. #define GDT_ENTRY_KERNEL_DS                (GDT_ENTRY_KERNEL_BASE + 1)
  9. #define __KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8)
復(fù)制代碼

把其中的宏替換成數(shù)值,則為:
  1. #define __USER_CS 115        [00000000 1110  0  11]
  2. #define __USER_DS 123        [00000000 1111  0  11]
  3. #define __KERNEL_CS 96      [00000000 1100  0  00]
  4. #define __KERNEL_DS 104    [00000000 1101  0  00]
復(fù)制代碼


方括號后是這四個段選擇符的16位二制表示,它們的索引號和T1字段值也可以算出來了
  1. __USER_CS              index= 14   T1=0
  2. __USER_DS               index= 15   T1=0
  3. __KERNEL_CS           index=  12  T1=0
  4. __KERNEL_DS           index= 13   T1=0
復(fù)制代碼


T1均為0,則表示都使用了GDT,再來看初始化GDT的內(nèi)容中相應(yīng)的12-15項(arch/i386/head.S):
  1.         .quad 0x00cf9a000000ffff        /* 0x60 kernel 4GB code at 0x00000000 */
  2.         .quad 0x00cf92000000ffff        /* 0x68 kernel 4GB data at 0x00000000 */
  3.         .quad 0x00cffa000000ffff        /* 0x73 user 4GB code at 0x00000000 */
  4.         .quad 0x00cff2000000ffff        /* 0x7b user 4GB data at 0x00000000 */
復(fù)制代碼


按照前面段描述符表中的描述,可以把它們展開,發(fā)現(xiàn)其16-31位全為0,即四個段的基地址全為0。

這樣,給定一個段內(nèi)偏移地址,按照前面轉(zhuǎn)換公式,0 + 段內(nèi)偏移,轉(zhuǎn)換為線性地址,可以得出重要的結(jié)論,“在Linux下,邏輯地址與線性地址總是一致(是一致,不是有些人說的相同)的,即邏輯地址的偏移量字段的值與線性地址的值總是相同的。。!”

忽略了太多的細節(jié),例如段的權(quán)限檢查。呵呵。

Linux中,絕大部份進程并不例用LDT,除非使用Wine ,仿真Windows程序的時候。

4.CPU的頁式內(nèi)存管理

CPU的頁式內(nèi)存管理單元,負責(zé)把一個線性地址,最終翻譯為一個物理地址。從管理和效率的角度出發(fā),線性地址被分為以固定長度為單位的組,稱為頁(page),例如一個32位的機器,線性地址最大可為4G,可以用4KB為一個頁來劃分,這頁,整個線性地址就被劃分為一個tatol_page[2^20]的大數(shù)組,共有2的20個次方個頁。這個大數(shù)組我們稱之為頁目錄。目錄中的每一個目錄項,就是一個地址——對應(yīng)的頁的地址。

另一類“頁”,我們稱之為物理頁,或者是頁框、頁楨的。是分頁單元把所有的物理內(nèi)存也劃分為固定長度的管理單位,它的長度一般與內(nèi)存頁是一一對應(yīng)的。

這里注意到,這個total_page數(shù)組有2^20個成員,每個成員是一個地址(32位機,一個地址也就是4字節(jié)),那么要單單要表示這么一個數(shù)組,就要占去4MB的內(nèi)存空間。為了節(jié)省空間,引入了一個二級管理模式的機器來組織分頁單元。文字描述太累,看圖直觀一些:

如上圖,
1、分頁單元中,頁目錄是唯一的,它的地址放在CPU的cr3寄存器中,是進行地址轉(zhuǎn)換的開始點。萬里長征就從此長始了。
2、每一個活動的進程,因為都有其獨立的對應(yīng)的虛似內(nèi)存(頁目錄也是唯一的),那么它也對應(yīng)了一個獨立的頁目錄地址!\行一個進程,需要將它的頁目錄地址放到cr3寄存器中,將別個的保存下來。
3、每一個32位的線性地址被劃分為三部份,面目錄索引(10位):頁表索引(10位):偏移(12位)
依據(jù)以下步驟進行轉(zhuǎn)換:
1、從cr3中取出進程的頁目錄地址(操作系統(tǒng)負責(zé)在調(diào)度進程的時候,把這個地址裝入對應(yīng)寄存器);
2、根據(jù)線性地址前十位,在數(shù)組中,找到對應(yīng)的索引項,因為引入了二級管理模式,頁目錄中的項,不再是頁的地址,而是一個頁表的地址。(又引入了一個數(shù)組),頁的地址被放到頁表中去了。
3、根據(jù)線性地址的中間十位,在頁表(也是數(shù)組)中找到頁的起始地址;
4、將頁的起始地址與線性地址中最后12位相加,得到最終我們想要的葫蘆;

這個轉(zhuǎn)換過程,應(yīng)該說還是非常簡單地。全部由硬件完成,雖然多了一道手續(xù),但是節(jié)約了大量的內(nèi)存,還是值得的。那么再簡單地驗證一下:
1、這樣的二級模式是否仍能夠表示4G的地址;
頁目錄共有:2^10項,也就是說有這么多個頁表
每個目表對應(yīng)了:2^10頁;
每個頁中可尋址:2^12個字節(jié)。
還是2^32 = 4GB

2、這樣的二級模式是否真的節(jié)約了空間;
也就是算一下頁目錄項和頁表項共占空間 (2^10 * 4 + 2 ^10 *4) = 8KB。哎,……怎么說呢!。

紅色錯誤,標(biāo)注一下,后文貼中有此討論。。。。。。
按<深入理解計算機系統(tǒng)>中的解釋,二級模式空間的節(jié)約是從兩個方面實現(xiàn)的:
A、如果一級頁表中的一個頁表條目為空,那么那所指的二級頁表就根本不會存在。這表現(xiàn)出一種巨大的潛在節(jié)約,因為對于一個典型的程序,4GB虛擬地址空間的大部份都會是未分配的;
B、只有一級頁表才需要總是在主存中。虛擬存儲器系統(tǒng)可以在需要時創(chuàng)建,并頁面調(diào)入或調(diào)出二級頁表,這就減少了主存的壓力。只有最經(jīng)常使用的二級頁表才需要緩存在主存中!贿^Linux并沒有完全享受這種福利,它的頁表目錄和與已分配頁面相關(guān)的頁表都是常駐內(nèi)存的。

值得一提的是,雖然頁目錄和頁表中的項,都是4個字節(jié),32位,但是它們都只用高20位,低12位屏蔽為0——把頁表的低12屏蔽為0,是很好理解的,因為這樣,它剛好和一個頁面大小對應(yīng)起來,大家都成整數(shù)增加。計算起來就方便多了。但是,為什么同時也要把頁目錄低12位屏蔽掉呢?因為按同樣的道理,只要屏蔽其低10位就可以了,不過我想,因為12>10,這樣,可以讓頁目錄和頁表使用相同的數(shù)據(jù)結(jié)構(gòu),方便。

本貼只介紹一般性轉(zhuǎn)換的原理,擴展分頁、頁的保護機制、PAE模式的分頁這些麻煩點的東東就不啰嗦了……可以參考其它專業(yè)書籍。

5.Linux的頁式內(nèi)存管理
原理上來講,Linux只需要為每個進程分配好所需數(shù)據(jù)結(jié)構(gòu),放到內(nèi)存中,然后在調(diào)度進程的時候,切換寄存器cr3,剩下的就交給硬件來完成了(呵呵,事實上要復(fù)雜得多,不過偶只分析最基本的流程)。

前面說了i386的二級頁管理架構(gòu),不過有些CPU,還有三級,甚至四級架構(gòu),Linux為了在更高層次提供抽像,為每個CPU提供統(tǒng)一的界面。提供了一個四層頁管理架構(gòu),來兼容這些二級、三級、四級管理架構(gòu)的CPU。這四級分別為:

頁全局目錄PGD(對應(yīng)剛才的頁目錄)
頁上級目錄PUD(新引進的)
頁中間目錄PMD(也就新引進的)
頁表PT(對應(yīng)剛才的頁表)。

整個轉(zhuǎn)換依據(jù)硬件轉(zhuǎn)換原理,只是多了二次數(shù)組的索引罷了,如下圖:

那么,對于使用二級管理架構(gòu)32位的硬件,現(xiàn)在又是四級轉(zhuǎn)換了,它們怎么能夠協(xié)調(diào)地工作起來呢?嗯,來看這種情況下,怎么來劃分線性地址吧!
從硬件的角度,32位地址被分成了三部份——也就是說,不管理軟件怎么做,最終落實到硬件,也只認(rèn)識這三位老大。
從軟件的角度,由于多引入了兩部份,,也就是說,共有五部份!尪䦟蛹軜(gòu)的硬件認(rèn)識五部份也很容易,在地址劃分的時候,將頁上級目錄和頁中間目錄的長度設(shè)置為0就可以了。
這樣,操作系統(tǒng)見到的是五部份,硬件還是按它死板的三部份劃分,也不會出錯,也就是說大家共建了和諧計算機系統(tǒng)。

這樣,雖說是多此一舉,但是考慮到64位地址,使用四層轉(zhuǎn)換架構(gòu)的CPU,我們就不再把中間兩個設(shè)為0了,這樣,軟件與硬件再次和諧——抽像就是強大呀。!

例如,一個邏輯地址已經(jīng)被轉(zhuǎn)換成了線性地址,0x08147258,換成二制進,也就是:
0000100000 0101000111 001001011000
內(nèi)核對這個地址進行劃分
PGD = 0000100000
PUD = 0
PMD = 0
PT = 0101000111
offset = 001001011000

現(xiàn)在來理解Linux針對硬件的花招,因為硬件根本看不到所謂PUD,PMD,所以,本質(zhì)上要求PGD索引,直接就對應(yīng)了PT的地址。而不是再到PUD和PMD中去查數(shù)組(雖然它們兩個在線性地址中,長度為0,2^0 =1,也就是說,它們都是有一個數(shù)組元素的數(shù)組),那么,內(nèi)核如何合理安排地址呢?
從軟件的角度上來講,因為它的項只有一個,32位,剛好可以存放與PGD中長度一樣的地址指針。那么所謂先到PUD,到到PMD中做映射轉(zhuǎn)換,就變成了保持原值不變,一一轉(zhuǎn)手就可以了。這樣,就實現(xiàn)了“邏輯上指向一個PUD,再指向一個PDM,但在物理上是直接指向相應(yīng)的PT的這個抽像,因為硬件根本不知道有PUD、PMD這個東西”。

然后交給硬件,硬件對這個地址進行劃分,看到的是:
頁目錄 = 0000100000
PT = 0101000111
offset = 001001011000
嗯,先根據(jù)0000100000(32),在頁目錄數(shù)組中索引,找到其元素中的地址,取其高20位,找到頁表的地址,頁表的地址是由內(nèi)核動態(tài)分配的,接著,再加一個offset,就是最終的物理地址了。

[ 本帖最后由 獨孤九賤 于 2009-9-22 20:36 編輯 ]
作者: ghosTM55    時間: 2008-01-15 16:37
標(biāo)題: 學(xué)習(xí)了

作者: netentsec    時間: 2008-01-15 16:48
深入淺出,講的好,期待下文
作者: sisi8408    時間: 2008-01-15 20:07
贊一個。。。。。。。。。
作者: duanius    時間: 2008-01-15 21:14
說的不錯   比ulk第二章更直白 人性化

[ 本帖最后由 duanius 于 2008-1-15 21:15 編輯 ]
作者: flw2    時間: 2008-01-16 09:34
支持一個,LZ超級高手
作者: achlice    時間: 2008-01-16 10:14
見了好帖子然后回復(fù) ,是一種美德, 比如像我,一直都 是這樣做的~~



看了內(nèi)核源代碼 情景 分析 ,從 8086 講到80286,  80386,  終于明白,這是怎么回事兒了~~

[ 本帖最后由 achlice 于 2008-1-28 15:53 編輯 ]
作者: qps104    時間: 2008-01-16 11:16
好文,加深了我對3種地址的理解
作者: yj1804    時間: 2008-01-16 17:20
好文,贊一個
PS.推薦大家看毛德操的"LINUX內(nèi)核源代碼情景分析"一書, 里面講解也比較詳細
作者: duanius    時間: 2008-01-16 18:51
好文好文  隱隱中透著一種和諧美
作者: liuweni    時間: 2008-01-17 08:48
mark一下,好貼。
作者: guohua219    時間: 2008-01-17 09:00
不錯,很能讓人理解.........
作者: Aryang    時間: 2008-01-17 11:16
還沒仔細看,大家說好那一定真的好
作者: 浮云一夢    時間: 2008-01-17 11:24
不錯不錯

作者: CUDev    時間: 2008-01-17 11:33
很好,很強大!
贊一個!
作者: xqwwqiao    時間: 2008-01-17 14:05
對我等新手來說,太深奧了!
作者: cindylzh    時間: 2008-01-17 14:27
lz理解真是透徹。仰望。。。。
作者: hinux    時間: 2008-01-17 17:31
不回不行啦!
作者: fwl    時間: 2008-01-17 18:14
不錯不錯  內(nèi)存管理的原理在os的教材上就是這么說的
不過和實際聯(lián)系起來的話就這個寫得最好了
作者: neoedmund    時間: 2008-01-17 23:46
你這圖是不是抄的深入理解linux內(nèi)核看著眼熟
作者: wqch    時間: 2008-01-18 00:21
大致看了一下,圖文并茂,講得不錯
作者: snhanwei    時間: 2008-01-18 09:06
如此好貼,當(dāng)然要頂!
作者: foxwolf_ym    時間: 2008-01-18 09:09
不錯,接著加深映像!!謝謝樓主!
作者: brvman    時間: 2008-01-18 09:29
好文,我頂!
作者: pl_piaoling    時間: 2008-01-18 12:31
看樣,還需要繼續(xù)學(xué)習(xí)~
作者: yamir    時間: 2008-01-18 14:44
原帖由 獨孤九賤 于 2008-1-15 16:32 發(fā)表

物理地址(physical address)
用于內(nèi)存芯片級的單元尋址,與處理器和CPU連接的地址總線相對應(yīng)。
——這個概念應(yīng)該是這幾個概念中最好理解的一個,但是值得一提的是,雖然可以直接把物理地址理解成插在機器上那根內(nèi)存本身,把內(nèi)存看成一個從0字節(jié)一直到最大空量逐字節(jié)的編號的大數(shù)組,然后把這個數(shù)組叫做物理地址,但是事實上,這只是一個硬件提供給軟件的抽像,內(nèi)存的尋址方式并不是這樣。所以,說它是“與地址總線相對應(yīng)”,是更貼切一些,不過拋開對物理內(nèi)存尋址方式的考慮,直接把物理地址與物理的內(nèi)存一一對應(yīng),也是可以接受的。也許錯誤的理解更利于形而上的抽像。



相當(dāng)不錯的文章,如果在物理地址這里能再說清楚點就完美了
把物理地址與物理內(nèi)存一一對應(yīng)在剛開始學(xué)的時候是可以接受,不過再更深入到x86的體系架構(gòu)中,這樣理解,容易會產(chǎn)生很多混淆了,還是應(yīng)該把北橋、IO端口和PCI一起拿出來理解,建議樓主可以結(jié)合Intel的北橋的datasheet里的那張System Address Ranges來補充說明一下。
作者: sanbiangongzi    時間: 2008-01-18 16:32
最后一部分沒有看懂,那位大俠再討論一下,唉,技不如人
作者: drunkedfish    時間: 2008-01-18 21:18
很強大  明天我估計要用它  感動啊
作者: 獨孤九賤    時間: 2008-01-19 21:26
原帖由 yamir 于 2008-1-18 14:44 發(fā)表


相當(dāng)不錯的文章,如果在物理地址這里能再說清楚點就完美了
把物理地址與物理內(nèi)存一一對應(yīng)在剛開始學(xué)的時候是可以接受,不過再更深入到x86的體系架構(gòu)中,這樣理解,容易會產(chǎn)生很多混淆了,還是應(yīng)該把北橋 ...


這涉及到DRAM的物理訪問方式了,本貼只是介紹偶對這些入門概念的理解而已,不想扯遠了……或許以后會單獨另一貼吧。

另有朋友說偶的圖的抄襲問題——不是抄襲,直接原版拷貝的,哈哈,圖畫得挺好,讓偶畫,肯定沒有這么簡單明了……
作者: wxs40305    時間: 2008-01-19 21:31
標(biāo)題: 回復(fù) #29 獨孤九賤 的帖子
獨孤九賤兄,能否幫我 解決 下我發(fā)的 "局部描述符 表 中有多少個描述符"貼?
謝謝了
作者: free_mind    時間: 2008-01-19 21:39
乖乖,嚇到我了,我還以為物理地址=線形地址,虛擬地址=邏輯地址
作者: 留下可愛心情    時間: 2008-01-19 21:59
很牛的說~!
作者: youyu_buzai    時間: 2008-01-20 15:14
不錯,,,,,是個高手
作者: tanbao2000    時間: 2008-01-20 15:30
學(xué)習(xí)了,頂一個。。。
作者: mageguoshi    時間: 2008-01-20 16:26
頂一個。∈呛锰泳鸵裘!
作者: vestige    時間: 2008-01-20 21:18
標(biāo)題: 回復(fù) #1 獨孤九賤 的帖子
感謝樓主分享!不過我對于這句話有點疑問:

2、這樣的二級模式是否真的節(jié)約了空間;
也就是算一下頁目錄項和頁表項共占空間 (2^10 * 4 + 2 ^10 *4) = 8KB。哎,……怎么說呢。!

這樣計算的結(jié)果應(yīng)該是所有頁目錄的空間+一個頁目錄對應(yīng)的頁表的空間,Linux進程內(nèi)存管理應(yīng)該可以允許進程不只一個頁目錄被裝入內(nèi)存吧?
還是說就是只能放1024個頁表?這樣就說的過去了。

[ 本帖最后由 vestige 于 2008-1-20 21:40 編輯 ]
作者: chenyq83    時間: 2008-01-20 22:33
謝謝樓主分享!
作者: xdolt    時間: 2008-01-20 23:36
這個社會終于“和諧”了!
作者: 獨孤九賤    時間: 2008-01-21 09:08
原帖由 vestige 于 2008-1-20 21:18 發(fā)表
感謝樓主分享!不過我對于這句話有點疑問:

2、這樣的二級模式是否真的節(jié)約了空間;
也就是算一下頁目錄項和頁表項共占空間 (2^10 * 4 + 2 ^10 *4) = 8KB。哎,……怎么說呢!!

這樣計算的結(jié)果應(yīng)該是 ...

是的,這樣計算的確是錯誤的,呵呵,謝謝指正,估計是當(dāng)時行文的時候,沒有仔細考慮……

二級模式能夠節(jié)約內(nèi)存容量的原因,當(dāng)一個進程被調(diào)度運行的時候,內(nèi)核會分配給它一個對應(yīng)的目錄項,但是由于采用虛擬內(nèi)存管理方式,這個目錄中的所有頁表并非都會被分配空間,只有進程實際需要的時候,才會進行頁表的空間分配。
作者: 獨孤九賤    時間: 2008-01-21 09:11
原帖由 wxs40305 于 2008-1-19 21:31 發(fā)表
獨孤九賤兄,能否幫我 解決 下我發(fā)的 "局部描述符 表 中有多少個描述符"貼?
謝謝了


沒有看到你的貼,不知你指的是不是這個:
  1. struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 },
  2.                 { 0, 0 }, { 0, 0 } };
復(fù)制代碼

arch/i386/kernel/traps.c
作者: ziyiu123    時間: 2008-01-21 11:22
與君共勉,呵呵,學(xué)習(xí)了。
作者: jonry    時間: 2008-01-21 17:34
很不錯,學(xué)習(xí)了~~~
作者: 想飛的蝸牛    時間: 2008-01-21 20:39
寫得很好 又容易看懂 呵呵 贊一個
lz是高手
作者: softchinacom    時間: 2008-01-22 14:19
標(biāo)題: 你的理解錯誤很多,居然將硬件的進步說成是冗余,可惜了。
從硬件設(shè)計的角度來看!
物理地址: 內(nèi)存單元的編號,邏輯上是將將內(nèi)存單元按照一定的度量單位劃分,如字節(jié),字等,80x86  可以按字節(jié)也可以按字編址。所謂的總線只是一個存取通路,插座相當(dāng)于一個設(shè)備開關(guān)。地址空間是數(shù)據(jù)總線的寬度。
邏輯地址:也稱相對地址,是與絕對地址(物理地址)相對的一個概念,是相對與某一位置開始編址。她存在的意義是記住程序和數(shù)據(jù)的相對位置。一般定義為CPU通過地址總線發(fā)出的地址。 程序裝入到內(nèi)存后就通過相關(guān)的硬件,主要是地址的加法器與一個相對位置相加得到絕對地址。相對的位置是存在段表和頁表中的,通過相聯(lián)查找獲得。地址空間由用戶定義
虛擬內(nèi)存:說白了就是構(gòu)造一個滿足程序和用戶需要的空間。程序員不用擔(dān)心內(nèi)存不夠!也不用擔(dān)心地址問題。只要關(guān)心程序的邏輯順序,即邏輯地址。
虛擬地址:和邏輯地址是抽象和具體的關(guān)系,是邏輯地址的一個實例。
虛擬地址到物理地址變換:對于X86  目前有三種方式,頁式,段式,段頁式
頁式:
地址映像:
   第一步 將用戶程序按頁的長度劃分,長度固定。  劃分了之后,就有了頁的起始地址
      第二步 操作系統(tǒng)創(chuàng)建進程時,先查全局的頁表,找了幾頁空閑的內(nèi)存,也就有了物理的頁的起始地址
      第三步 操作系統(tǒng)創(chuàng)建頁表,一般有這么幾項,物理頁號, 用戶頁號 (o-n) 使用位等!∵@樣就建立了虛擬地址到物理地址的映射關(guān)系了。
地址變換:硬件根據(jù)cpu發(fā)出的邏輯地址(這里是虛擬地址)查表,得到物理的頁號(高位地址),將物理頁號加邏輯地址的地位(頁內(nèi)地址)就是物理地址了。一次查表,一次加法計算。比直接用物理地址編程效率要低很多。
段式:
虛擬地址劃分  用戶號+段號+段內(nèi)偏移
地址映像:
   第一步 將用戶程序分段,長度不固定。  劃分了之后,就有了段的起始地址,和段的長度。
      第二步 操作系統(tǒng),根據(jù)段長申請內(nèi)存空間,也就有了段起始地址。
      第三步 操作系統(tǒng)創(chuàng)建段表,一般有這么幾項,兩個段起始地址, 段長!∵@樣就建立了邏輯段到物理段的映射關(guān)系了。
地址變換:INTEL X86是這樣的, 有段寄存器堆,即段選擇子,描述符是一致的。 先將段表基地址裝入 LDT,(這里討論的是一般用戶程序),根據(jù)CPU 發(fā)出的邏輯地址得到段號, 加上LDT 當(dāng)中的段表基地址得到一個邏輯段到物理段的映射項,讀出物理段基地址,再加上段的偏移得到 物理地址 ,總共需要做兩次加法,一次查表, 效率更低。
段頁式: 即將程序中一段再分頁,需要一個段表,一個頁表,先查段表得到頁表基地址,然后再頁式變換。
對于這種三種變換方式的優(yōu)點: 簡化 編程,簡化系統(tǒng)設(shè)計,太多了,
總結(jié):  對內(nèi)核編程人員的建議:  先對程序劃分,再定義映射,設(shè)置寄存器,地址變換由硬件完成。
作者: polter    時間: 2008-01-22 15:46
好文,標(biāo)記一下,贊一個
作者: mik    時間: 2008-01-23 01:17
原帖由 softchinacom 于 2008-1-22 14:19 發(fā)表
從硬件設(shè)計的角度來看!
物理地址: 內(nèi)存單元的編號,邏輯上是將將內(nèi)存單元按照一定的度量單位劃分,如字節(jié),字等,80x86  可以按字節(jié)也可以按字編址。所謂的總線只是一個存取通路,插座相當(dāng)于一個設(shè)備開關(guān)。地址空 ...


沒細看,但 LZ 寫得還是不錯滴,何以說錯誤很多呀,你說得也一踏糊涂
作者: 獨孤九賤    時間: 2008-01-23 09:24
原帖由 softchinacom 于 2008-1-22 14:19 發(fā)表
從硬件設(shè)計的角度來看!
物理地址: 內(nèi)存單元的編號,邏輯上是將將內(nèi)存單元按照一定的度量單位劃分,如字節(jié),字等,80x86  可以按字節(jié)也可以按字編址。所謂的總線只是一個存取通路,插座相當(dāng)于一個設(shè)備開關(guān)。地址空 ...


1、如果你認(rèn)為段式+頁式不是Intel的冗余,不是為了向上兼容而保留,而是一種硬件進步,我雖然不贊成你的看法,但是我想聽聽你的理由——不過你的貼子中,并沒有并于“進步”的原因解釋,只是一大堆的名詞解釋。呵呵。而且,內(nèi)容基本與偶是一致的,沒有質(zhì)的分歧;

2、Intel在后續(xù)CPU中,保留了段式映射結(jié)構(gòu),連他自己都說這只是一個兼容而不得已的原因(這個不權(quán)威哈,出處忘了,好像在N年前的一篇電腦雜志上看到的)

3、很多優(yōu)秀的CPU,本身就沒有,或者說是“有限支持”段式映射;

4、如果真要說是“進步”,那也是Intel一家的進步,從段式發(fā)展到支持頁式——不過如果它不考慮兼容,完全可以舍棄段式。所以還是說不上“進步”。

呵呵……一家之言,僅供參考。
作者: df00171    時間: 2008-01-24 00:17
lz寫的好啊
作者: jzlinux    時間: 2008-01-24 09:57
標(biāo)題: 回復(fù) #1 獨孤九賤 的帖子
好文,mark一下,感謝lz
作者: jiaxi    時間: 2008-01-24 10:26
好文,轉(zhuǎn)載了
謝謝
作者: DeathAngle    時間: 2008-01-24 10:40
標(biāo)題: 回復(fù) #1 獨孤九賤 的帖子
good,轉(zhuǎn)載下,幫 lz再宣傳下
作者: flytiger23    時間: 2008-01-24 11:57
樓主描述得很精彩,贊一個,頂一個,呵呵
作者: caong    時間: 2008-01-24 15:57
好帖,希望更多人能看到
作者: dreampro    時間: 2008-01-26 08:25
提示: 作者被禁止或刪除 內(nèi)容自動屏蔽
作者: gbh5526335    時間: 2008-01-27 22:34
感覺講的不錯,可是我看不懂,基礎(chǔ)太薄弱了!
作者: gothicane    時間: 2008-01-28 10:14
好貼~~收藏了~~~~~
作者: 戀夏寒    時間: 2008-01-28 17:54
mark 一下, 留個腳印
作者: 雪夜狂飄    時間: 2008-01-29 10:57
的確值得學(xué)習(xí),有時間應(yīng)該研究研究
作者: www1862    時間: 2008-01-29 11:55
不錯,都是樓主自己的理解。圖抄一抄沒什么的,哈哈。注明一下就行了。
作者: mageguoshi    時間: 2008-02-01 14:17
好帖留名!一定要頂啊!
作者: mouse2000    時間: 2008-02-15 20:51
好文是一定要頂?shù)膥!
作者: chameleon110    時間: 2008-02-18 02:18
好貼,深入淺出,頂,堅決要頂
作者: william.zhang    時間: 2008-02-18 11:18
決定頂下:em11:
作者: 大塊頭    時間: 2008-02-19 13:36
據(jù)我所知,九見好像是圈里的,哈哈
作者: 獨孤九賤    時間: 2008-02-19 16:23
標(biāo)題: 回復(fù) #64 大塊頭 的帖子
什么圈(quan or juan)?
作者: maxxfire    時間: 2008-02-19 17:01
好文哪,期待樓主的下一篇~
樓主,能不能講講linux內(nèi)存3個zone:DMA、NORMAL、HIGHMEM的工作原理啊。。
作者: arshur    時間: 2008-02-21 13:59
標(biāo)題: 強人啊。。。
強人。。。。。。。。。。。。。!
作者: fdshenjia    時間: 2008-02-21 19:41
寫的太好了!不想說太多的贊美,但是發(fā)自內(nèi)心的。
作者: pasoolee    時間: 2008-02-22 12:29
樓主應(yīng)該結(jié)合 Linux下的 IA32匯編語言,講一下,或結(jié)合PE文件的特性,在深入探討一下,真正的可執(zhí)行文件是如何編譯的,如何加載的,如:PE文件的入口地址如何映射的問題,嘻嘻。(當(dāng)然,我可不知道編譯器的內(nèi)部原理哦,只是想說一下如何對靜態(tài)代碼進行分析,不包括對靜態(tài)代碼加密的哦)
作者: potti_chu    時間: 2008-02-22 15:31
很好,很強大. 來學(xué)習(xí).
作者: renxiaojun_6635    時間: 2008-02-22 21:44
看過,不錯! 學(xué)到了!
作者: _LoveLinux    時間: 2008-02-23 11:44
好文,LZ很厲害!!

[ 本帖最后由 _LoveLinux 于 2008-2-23 11:55 編輯 ]

61254_080216010548.gif (14.25 KB, 下載次數(shù): 81)

61254_080216010548.gif

作者: aaaaal    時間: 2008-02-26 09:38
佩服
光從排版的精神來說都值得稱贊。
作者: wangjl_sdu    時間: 2008-02-27 09:54
看了,不錯,收下了。
作者: Godbach    時間: 2008-03-06 23:18
感謝LZ的辛苦勞動
作者: bigbear1113    時間: 2008-03-07 22:57
好文章,很值得學(xué)習(xí)!!
作者: wwling2001    時間: 2008-03-09 19:26
標(biāo)題: 回復(fù) #1 獨孤九賤 的帖子
好帖啊,頂一下
作者: davidwrk    時間: 2008-03-10 13:22
學(xué)習(xí)了~學(xué)習(xí)了~學(xué)習(xí)了~學(xué)習(xí)了~
作者: vestige    時間: 2008-03-29 12:42
標(biāo)題: 回復(fù) #1 獨孤九賤 的帖子
我對這里還有一點疑問:
從軟件的角度上來講,因為它的項只有一個,32位,剛好可以存放與PGD中長度一樣的地址指針。那么所謂先到PUD,到到PMD中做映射轉(zhuǎn)換,就變成了保持原值不變,一一轉(zhuǎn)手就可以了。這樣,就實現(xiàn)了“邏輯上指向一個PUD,再指向一個PDM,但在物理上是直接指向相應(yīng)的PT的這個抽像,因為硬件根本不知道有PUD、PMD這個東西”。

我對“轉(zhuǎn)手”這個過程不太了解,是不是一個32位的線性地址,先從頭10位的PGD中取得PT的入口項,然后由于要兼容4級分頁,需要把這個32位的線性地址拷貝到唯一的PUD入口,再拷貝到PMD入口,然后再得到PT的地址?這樣做的話豈非效率很低?
作者: zx_wing    時間: 2008-03-29 12:58
原帖由 vestige 于 2008-3-29 12:42 發(fā)表
我對這里還有一點疑問:
從軟件的角度上來講,因為它的項只有一個,32位,剛好可以存放與PGD中長度一樣的地址指針。那么所謂先到PUD,到到PMD中做映射轉(zhuǎn)換,就變成了保持原值不變,一一轉(zhuǎn)手就可以了。這樣,就 ...

不是,對頁表的操作,是逐級獲得指向下一級的指針,對于PUD的操作,實際上仍然是返回指向PGD的指針。
這里有段代碼,雖然不是x86頁表操作的代碼,但性質(zhì)是一樣的,可以解釋這個問題。
/*
代碼中所有和pud有關(guān)的部分都是假的,被實現(xiàn)成空函數(shù),例如pud_alloc_one()、pgd_cmpxchg_rel()等等
*/
pgd = pgd_offset(mm, mpaddr);
again_pgd:
    if (unlikely(pgd_none(*pgd))) { // acquire semantics

        pud_t *old_pud = NULL;
        pud = pud_alloc_one(mm, mpaddr);
        if (unlikely(!pgd_cmpxchg_rel(mm, pgd, old_pud, pud))) {
            pud_free(pud);
            goto again_pgd;
        }
    }

    pud = pud_offset(pgd, mpaddr); //這里仍然返回指向PGD的指針
again_pud:
    if (unlikely(pud_none(*pud))) { // acquire semantics

        pmd_t* old_pmd = NULL;
        pmd = pmd_alloc_one(mm, mpaddr);
        if (unlikely(!pud_cmpxchg_rel(mm, pud, old_pmd, pmd))) {
            pmd_free(pmd);
            goto again_pud;
        }
    }

    pmd = pmd_offset(pud, mpaddr);
again_pmd:
    if (unlikely(pmd_none(*pmd))) { // acquire semantics

        pte_t* old_pte = NULL;
        pte_t* pte = pte_alloc_one_kernel(mm, mpaddr);
        if (unlikely(!pmd_cmpxchg_kernel_rel(mm, pmd, old_pte, pte))) {
            pte_free_kernel(pte);
            goto again_pmd;
        }
    }


作者: vestige    時間: 2008-03-29 15:49
標(biāo)題: 回復(fù) #80 zx_wing 的帖子
Thanks 樓上 a lot!

我看ULK里講到:
The kernel keeps a position for the Page Upper Directory and the Page Middle Directory by setting the number of entries in them to 1 and mapping these two entries into the proper entry of the Page Global Direcotry.

不知道這里說的proper entry指的什么?
是不是為PUD和PMD在PGD中分配的兩個entry,然后這兩個entry又都指回了PGD?
作者: zx_wing    時間: 2008-03-29 18:50
原帖由 vestige 于 2008-3-29 15:49 發(fā)表
Thanks 樓上 a lot!

我看ULK里講到:
The kernel keeps a position for the Page Upper Directory and the Page Middle Directory by setting the number of entries in them to 1 and mapping these ...

我也不清楚這里的2級paging是什么意思。
我對x86的這部分不是很懂,但剛才去看了一下,確實分配了pmd的,應(yīng)該是三級level。我看的代碼是2.6.20,不知道這個版本還支持2級level不。不太懂,不好意思
作者: vestige    時間: 2008-03-29 19:33
還是感謝zx_wing了,呵呵,我去琢磨琢磨,搞明白了整理個東西出來
作者: hansom6    時間: 2008-03-29 20:24
學(xué)習(xí)了,
作者: new_learner    時間: 2008-04-13 02:29
樓主描述得不錯,生動形象,呵呵
作者: capable    時間: 2008-04-13 12:26
very good!
作者: pro_mise    時間: 2008-04-13 16:29
呵呵,九賤兄幸苦了啊。
to 44:
您提到總線地址是邏輯地址?在x86平臺,對應(yīng)PCI總線,總線地址應(yīng)該是物理地址吧?
作者: szjrabbit    時間: 2008-05-31 23:21
深入淺出,確實是好文,期待作者有更好的文章,新手需要這樣的文章。
作者: minifish    時間: 2008-06-01 10:08
原帖由 pro_mise 于 2008-4-13 16:29 發(fā)表
呵呵,九賤兄幸苦了啊。
to 44:
您提到總線地址是邏輯地址?在x86平臺,對應(yīng)PCI總線,總線地址應(yīng)該是物理地址吧?


這要看44樓提到的地址總線是哪一個級別的?如果是CPU to MMU的話,倒也不能說錯。
如果是FSB或者PCI總線上的地址,那么肯定是物理地址。
嗯,嚴(yán)格的說,F(xiàn)SB上發(fā)出的物理地址不一定完全是內(nèi)存的物理地址
可以去看看Intel MCH的規(guī)范,里面講的比較詳細。
作者: zmhu    時間: 2008-06-03 22:44
very good. study !!!
作者: qingfengjianke    時間: 2008-06-28 09:57
:wink: 55555
作者: 怪怪虎    時間: 2008-06-30 09:38
標(biāo)題: 回復(fù) #1 獨孤九賤 的帖子
好帖啊,頂起來~~~~
作者: james_li85    時間: 2008-07-08 16:58
正在看內(nèi)核
強烈頂LZ
作者: hansion3406    時間: 2008-07-11 10:44
這個竟然是我不懂的。。
今天我一定看明白它再下班。。
作者: 冰伊羅    時間: 2008-07-11 18:18
強,本人新手,學(xué)習(xí)ing
作者: 黑胡子    時間: 2008-11-20 09:06
看懂了3分之1  后面就:em12:
作者: linuxfun    時間: 2008-12-23 10:57
提示: 作者被禁止或刪除 內(nèi)容自動屏蔽
作者: vincentdpan    時間: 2008-12-26 15:08
有啥好說的,加精!
作者: whoisliang    時間: 2008-12-28 08:22
學(xué)習(xí)了,感動!
作者: 隨風(fēng)之幻    時間: 2009-01-04 14:14
嗯,先根據(jù)0000100000(32),在頁目錄數(shù)組中索引,找到其元素中的地址,取其高20位,找到頁表的地址,頁表的地址是由內(nèi)核動態(tài)分配的,接著,再加一個offset,就是最終的物理地址了。


頁表的地址是由內(nèi)核動態(tài)分配的?這句話有問題吧。因為在linux內(nèi)核代碼head.s里面,頁目錄和頁表早就把整個內(nèi)存劃分好的。不存在動態(tài)分配一說。

再說頁目錄的地址是在物理地址0處,跟在頁目錄后面的就是頁表,我實在是無法理解LZ說的頁表的地址由內(nèi)核動態(tài)分配的意思。

[ 本帖最后由 隨風(fēng)之幻 于 2009-1-4 14:17 編輯 ]




歡迎光臨 Chinaunix (http://72891.cn/) Powered by Discuz! X3.2