日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux内核 之 GDT LDT与分段

發布時間:2023/12/18 linux 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux内核 之 GDT LDT与分段 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

全局描述符表GDT(Global Descriptor Table)

在整個系統中,全局描述符表GDT只有一張(一個處理器對應一個GDT),GDT可以被放在內存的任何位置,但CPU必須知道GDT的入口,也就是基地址放在哪里,Intel的設計者門提供了一個寄存器GDTR用來存放GDT的入口地址,程序員將GDT設定在內存中某個位置之后,可以通過LGDT指令將GDT的入口地址裝入此寄存器,從此以后,CPU就根據此寄存器中的內容作為GDT的入口來訪問GDT了。

GDTR中存放的是GDT在內存中的基地址和其表長界限。而GDT存放的是段描述符。一般8個字節。在Linux中,【Base Address,Limit,Access】,他們加在一起被放在一個64bit長的數據結構中,被成為段描述符,但是intel為了向后兼容,將段基址寄存器仍然規定為16bit,很明顯,我們無法使用16bit長度的段寄存器來引用64bit的段描述符。怎么辦呢?解決的辦法是把這些長度為64bit的段描述符放入一個數組中,而將段寄存器中的值作為下標索引來間接引用(事實上就是將段寄存器中的高13bit的內容作為索引)。這個全局數據就是GDT。事實上GDT中存放的不僅僅是段描述符,還有其他描述符,他們都是64bit長。而邏輯地址的基地址就是16bit就可以了,不必64bit。

段選擇子:

是處理器將索引值乘以8的,不是自己,另外還要右移三位,例如是第二段吧,首先是0000 0000 0000 0000,只用index的話,就是0000 0000 0001 0000最后是0x0010,實際上也是乘以8啊

這是兩個4字節,

當我們要訪問某個段中的一個地址時候:
1.由GDTR得到段描述符表GDT
2.從段選擇子中的前13位得到我們要訪問的段的描述符在段描述符表中的索引(Index)(需要考慮TI和RPL)
3.從段描述符表中得到要訪問的段的描述符,得到其基地址
4.基地址加上偏移地址就是我們要訪問的內存地址(當然這里是虛擬地址,接下來是分頁機制的功能將虛地址轉換為物理地址,不做討論。)

我們來看一個例子吧:
給出邏輯地址:33h:12345678h轉換為線性地址
1.段選擇子SEL=33h=0000000000100 0 01b
前13bit是4,則在GDT中選擇第四個描述符;TI=0代表是在GDT中選擇,
后邊的特權級RPL=1,假設第四個段描述符的基地址為11111111h
2.OFFSET=12345678h,則線性地址為:base+offset=11111111h+12345678h=123456789h
可以看出,邏輯地址的基地址并不是真正的基地址了,而是索引+權限的集合,一共16位。每個邏輯地址由16位的段選擇符+32位的偏移量組成。

LDT相當于二級描述符表,存在GDT中,我們還是來看一個例子吧:
給出邏輯地址:37h:12345678h轉換為線性地址
1.段選擇子:37h=0000000000100 1 01b,最低第三bit是1,在LDT中選擇
2.首先,從GDTR中得到GDT表
3.從以LDTR寄存器為GDT的段選擇子,用LDTR的前13bit得到LDT在GDT中的索引,得到LDT的基址(假設為4)
4.使用段選擇子37h的前13bit在LDT表中得到LDT描述符(假設是3),從而得到LDT段描述符的基址(假設為11111111h)
5:基地址加上偏移地址12345678得到線性地址:23456789h
這里每個LDT是不同的,因此每一個程序都會不同吧,主要是,LDTR肯定不知道的。可能每一個程序的LDTR自動修改。在Linux中,邏輯地址等同于線性地址,在不分頁的情況下,線性地址就是物理地址。而且也沒有LDT表。這個下面講:

在保護模式下,80386虛地址空間可達16K個段,每段大小可變,最大達4GB。邏輯地址到線性地址的轉換由80386分段機制管理。段寄存器CS、DS、ES、SS、FS或GS各標識一個段。這些段寄存器作為段選擇器,用來選擇該段的描述符。段選擇子,就是這么回事。

前面說了那么多關于分段機制的實現,其實,對于Linux來說,并沒有什么卵用。Linux對80386的分段機制使用得很有限,因為Linux的設計目標是支持絕大多數主流的CPU,而很多CPU使用的是RISC體系結構,并沒有分段機制,所以為了讓 Linux 具有更好的可移植性,最好是去掉段機制而只使用分頁機制。但不幸的是,IA32規定段機制是不可禁止的,首先,我們要明確,分段機制是IA32提供的尋址方式,這是硬件層面的。就是說,不管你是windows還是linux,只要使用IA32的CPU訪問內存,都要經過MMU的轉換流程才能得到物理地址,也就是說必須經過邏輯地址–線性地址–物理地址的轉換。因此不可能繞過它直接給出線性地址空間的地址。萬般無奈之下,Linux的設計人員干脆讓段的基地址為0,而段的界限為4GB,這時任意給出一個偏移量,則等式為“0+偏移量=線性地址”,也就是說“邏輯地址中的偏移量=線性地址”。也就是說,給一個進程4G的內存空間也好,還是給一個段4G的內存空間也好,一般是給一個進程4G空間,說給一個段4G空間也不差,這只是對分段的妥協。所以2.6版內核只有在80x86結構下才使用分段,而且只是象征性地使用了一下,因為Linux基本不使用分段的機制,或者說,Linux中的分段機制只是為了兼容IA32的硬件而設計的。Linux內核的設計并沒有全部采用Intel所提供的段方案,僅僅有限度地使用了一下分段機制。這不僅簡化了Linux內核的設計,而且為把Linux移植到其他平臺創造了條件。由于IA32段機制還規定,必須為代碼段和數據段創建不同的段,所以Linux必須為代碼段和數據段分別創建一個基地址為0,段界限為4GB的段描述符。不僅如此,由于Linux內核運行在特權級0,而用戶程序運行在特權級別3,根據IA32段保護機制規定,特權級3的程序是無法訪問特權級為0的段的,所以Linux必須為內核用戶程序分別創建其代碼段和數據段。這就意味著Linux必須創建4個段描述符——特權級0的代碼段和數據段,特權級3的代碼段和數據段,下表顯示了這四個重要段的段描述符字段的值:

看見沒,都是0,相應的段描述符由宏__USER_CS,__USER_DS,__KERNEL_CS,和__KERNEL_DS分別定義。例如,為了對內核代碼段尋址,內核只需要把這個宏產生的值裝進cs段寄存器即可。?注意,與段相關的線性地址從0開始,達到232?-1的尋址限長。這就意味著在用戶態或內核態下的所有進程可以使用相同的邏輯地址。所有段都從0x00000000開始,這可以得出另一個重要結論,那就是在Linux下邏輯地址與線性地址是一致的,即邏輯地址的偏移量字段的值與相應的線性地址的值總是一致的。Linux的進程中數據段和代碼段都是0開始的,都能擴展到4GB,根據其他的不同在頁表中區分開。每個段的頁表都不同,在線性轉物理地址的時候,它能知道這是哪一個段的頁表,從而進行轉換,頁表是實現虛擬內存映射的重要機制,就算不分頁,它也是需要進行映射處理的。在保護模式下,控制寄存器CR0的最高位PG位控制著分頁管理機制是否生效,如果PG=1,分頁機制生效,需通過頁表查找才能把線性地址轉換物理地址。如果PG=0,則分頁機制無效,線性地址就直接做為物理地址。邏輯地址直接變成物理地址?;旧暇褪且豁?K。

內核態地址對應的相關頁表項,對于所有進程來說都是相同的(因為內核空間對所有進程來說都是共享的),而這部分頁表內容其實就來源于“內核頁表”,即每個進程的“進程頁表”中內核態地址相關的頁表項都是“內核頁表”的一個拷貝。用戶進程會進入內核從而訪問內核。這就是系統調用。

頁目錄表的大小為4k(剛好是一個頁的大小),包含1024項,每個項4字節(32位),項目里存儲的內容就是頁表的物理地址。如果頁目錄表中的頁表尚未分配,則物理地址填0。

頁表的大小也是4k,同樣包含1024項,每個項4字節,內容為最終物理頁的物理內存起始地址??梢哉f除了低一級,就沒什么區別。頁表內容是不連續的,因為頁肯定是不連續的,這是它的意義,而頁目錄表連續不連續,就要看頁表連續不連續,一般是不連續的,那么頁目錄表就是不連續的,一般一個進程一個頁目錄表,因為頁表肯定是依附進程的。每個活動的任務,必須要先分配給它一個頁目錄表,并把頁目錄表的物理地址存入cr3寄存器。頁表可以提前分配好,也可以在用到的時候再分配。Linux采用了四級頁表來管理內存頁,多級頁表就是把內存分成區塊來管理,將原來的映射關系改成區塊索引和區塊內的偏移。由于虛擬內存空間通常只用了很少一部分,那么,多級頁表就只保存這些使用中的區塊,這樣就可以大大地減少頁表的項數。

舉個例子:

線性地址?0x80495b0?轉換成二進制后是?0000 1000 0000 0100 1001 0101 1011 0000,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 頁目錄表索引? ? ? ? ?頁表索引? ? ? ? 偏移量

最高10位0000 1000 00的十進制是32,CPU查看頁目錄表第32項,里面存放的是頁表的物理地址。線性地址中間10位00 0100 1001?的十進制是73,頁表的第73項存儲的是最終物理頁的物理起始地址。物理頁基地址加上線性地址中最低12位的偏移量,CPU就找到了線性地址最終對應的物理內存單元。

虛擬地址轉換是用頁機制完成的,虛擬地址分布如下:

這解釋了為什么程序代碼會從0開始。

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

總結

以上是生活随笔為你收集整理的Linux内核 之 GDT LDT与分段的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。