內核第二講,內存保護的實現,以及知識簡介,局部描述符,全局描述符.


 

        內核第二講,內存保護的實現,以及知識簡介,局部描述符,全局描述符.

一丶了解80386的各種模式

80386,也就是32位系統下,有三種模式需要了解一下.

實模式,保護模式.虛擬86模式

實模式: 指的是操作系統在啟動的是否,這時候訪問的內存都是實際的物理內存.而在這個是否,操作系統會填寫內核中的內中表格.比如今天講的表(全局描述符表  GDT)

 

保護模式: 當各種表填寫好了,那么我們的內存也被保護了.這個是否我們的進程就不會直接訪問物理地址了.進而產生了保護行為,我們的內存就有了 可讀 可寫,可執行一說了.

虛擬86模式: 操作系統啟動的是否,運行的都是實際的16位匯編.那么現在我們假設有一個16位程序要啟動.那么修改了我們物理地址的內存,那么保護模式不就沒用了.所以為了防止這一情況的產生,操作系統做了一個虛擬86模式,也就是說可以運行16位匯編程序.

關於更詳細的介紹,請下載當講課堂資料的  "8086處理器的工作模式.doc"文件進行查看.

二丶保護模式如何保護內存的.

我們通過上面模式的介紹,知道的操作系統啟動的是否會從實模式啟動,然后會切換到保護模式.

那么是如何保護內存的.

比如我們要保護 100地址的內存,讓其支持可以讀,不可以寫.那么在 ring3下有匯編代碼

mov [100],10  

如何保證它不被改寫.

思路一:

  操作系統作者進行各種判斷,判斷這個地址不可以寫.

思路二:

  留下接口,比如我們編寫SDK程序的是否,當鼠標點擊的是否,會產生一個回調函數,那么這個回調函數是用戶寫的.因為當鼠標按下,操作系統只能通知你,但是不知道你要怎么做.

思路三:

  做表,通過查表,進而判斷內存是否可以訪問或者讀寫.

這里關於前兩種思路,第一種思路,對於操作系統肯定不現實的.每次訪問內存都要判斷,校驗,不說你能不能完成,就算真的完成了,那么你的操作系統也會奇卡無比.

思路二,思路二放在我們自己編寫的是否倒是可以.但是對於操作系統來說也是不現實的.

思路三,這個可以了.我們可以通過查表的方法,進行判斷.而且只在訪問內存的是否才進行判斷內存是否可讀寫.

 

對於上面來說,我們就會產生一個新的疑問.這個表怎么做才合理.

在做表之前,我們要熟悉匯編中的段寄存器. 我們知道,在16位匯編中,我們可以通過段+偏移的方式來尋找內存.管理內存.那么我們現在要對內存做管理.那么就要分段了.

三丶分段管理概要

進行分段管理,來管理內存.那么我們應該怎么分.在分段之前我們要理解幾個基本的概念.

1.邏輯地址.什么是邏輯地址

2.線性地址.什么是線性地址.

3.物理地址.

4.虛擬地址(暫時不講)

邏輯地址: 邏輯地址指的是 段 + 偏移的方式,我們程序中每一行代碼都是邏輯地址.

物理地址: 物理地址就是內存條的地址,也就是我們說的實際地址.

線性地址: 線性地址我們可以理解為邏輯上連續的物理地址.

段+偏移 查表,會查到線性地址.(物理地址),每個段+偏移都會查到一塊物理地址.

比如我們的邏輯地址:  00401000~00402000,在邏輯上我們看的是連續的,但是通過查表轉換為物理地址的是否則不是連續的.

看圖:

有可能進過查表,得出的物理地址不是連續的.但是邏輯地址是連續的.

線性地址,如果我們沒有虛擬內存.那么查到的線性地址就是物理地址.如果有虛擬內存,那么可能還要查表才能轉化為物理內存.

 

為什么要查表得到物理地址?

  原因是,我們要對進程做隔離,對內存做保護.所以我們查到對應的物理地址其實是對它做一個保護.

四丶表格式怎么做.怎么做表?

既然上面我們明白了,要對內存做保護,那么首先要分段,對每一段內存做保護.那么該怎么設計.

1.段起始地址

2.大小

3.結束地址.

4.當前內存的保護屬性.

我們會這樣設計表格.其實我們想到這樣設計,那么inter等等CPU也不是神,也會這樣設計.

看下Inter設計的表格.

每錯,第一次看到就會暈.那么仔細講解這個存儲段描述符表

首先我們理解一下 Base Address,也就是段.

段是實現虛擬地址到線性地址(物理地址)的轉化機制的基礎,也就是查表需要用到段.

那么在保護模式下.每個段有三個參數. 段基地址(Base Address) 段界限(limit),可以理解為區域

段屬性(Attributes) 這三個字段則是inter定義的表格.和我們的差不多.  

Base Address:  段基地址,也就是我們分段的是否,在32位下,段基地址長32位.也就代表的我們可以分配4G個段.這些了解即可.

 

段界限(Limit): 段界限,指的就是當前內存起始位置加上當前段界限的大小,是屬於什么屬性的.這個在段屬性中有表明.段界限用20位來表示,注意,是位.段界限可以是以字節為單位,也可以以4K為單位.什么意思那? 也就是說.這個段界限最大是20位表示  那么界限就是 2^20次方-1*4096字節大小

計算得出:

  2^20-1 * 4K  = FFFFF000,然而在32為系統中不是4G大小的界限.所以進而加上0FFFH大小.

公式: LIMIT = limit(2^20) * 4k + 0FFFh.  也相當於 左移<<12位,+0FFFh.

而是否*4K則看屬性中的G位,如果G位為1,那么就*4K,如果為0,則*8位,也就是按照一個字節的界限去做.

 

4.1 線性地址的范圍怎么計算.

base Address + limit 則可以形成一塊內存區域.

假設段A的基地址是  00012345h,段界限為5678h,並且是以字節為單位(G = 0),那么

段基地址+ 段界限 = 000179BDh, 那么在00012345h ~ 000179BDH則是段A的線性地址

計算就是  段基地址 + 段界限 (也可以理解為,起始地址+一塊區域.則這塊區域就是線性地址)

如果屬性中 G位 = 1,那么就是按照4K去計算.

 

0012345h + (5678 *4K)  + 0FFFh = 0568b344h

那么段A的線性地址就是從 0012345h ~ 0568b344h

 

我們上面計算線性地址,提供了一個表格.這個表格相信大家現在也能看懂了.

其中,我們知道了 段基地址占4個字節(32位)而段界限占20位,那么還屬性則占12位,而一種表的大小是8個字節.

看上面的圖,我們發現

M+7 存儲的是段地址

M+4,M+3,M+2 這三個字節也是存儲的段的地址.這是為什么? 因為inter為了兼容80286(24)位,那么只能添加新的東西來了,而在80286上面,是沒有多加的.

 

M+1 M+0 這兩個字節是16位,存儲的是段界限,而M+6的8位也是存儲的段界限的.

而M+5,M+6中.M+5全部都是段的屬性,M+6中,也有段屬性的存在.

M+6,和M+5展開查看.

其中 BLT3,BLT2,BLT1,BLT0這4位,是段界限的4位.剩余的是段屬性

 

4.2段屬性介紹.

根據上面的表展開得知,段屬性分別有

G D 0 AVL  P DPL DT1  TYPE

那么分別是什么意思?

G : 歷史位, 當G = 1的是否,段界限需要 * 4K(0-4095),G = 0的是否,那么就是按照8位來計算,也就是一個字節.

D: D位方向位.我們的堆棧保護內存的時候,堆棧和我們正常的數據結構是相反的.段界限我們應該+那么堆棧就需要減,所以需要個方向位調整

BLT5, 保留位

AVL: 應用軟件可使用位.

P: 這個段這個內存是否有效

DPL: 特權位,判斷你是0環還是3環

DT位: 描述我們這個表的類型是用戶還是系統.DT = 1描述系統. 

TYPE:位,這個則是表明了內存屬性是可讀,可寫,還是可執行.

TYPE位展開.

如果想要一塊內存可讀可執行,可寫.那么需要建立兩個表.分別讓TYPE為 = 2,然后 = 8即可.

因為段可以重疊.所以可以這樣操作,所以就有了修改內存保護屬性的API,inter官方承認的.

五丶什么是描述符,以及全局描述符表,局部描述符表.

描述符:

  也就是我們說的三個字段.我們這三個字段合在一起成為了一張表.這個表則成為存儲段描述符. 存儲段信息的表.

局部描述符表:

   我們知道針對我們一個進程我們可以建立多個存儲段描述符表.來保護我們的內存,那么CPU訪問的是否則會進行校驗.

那么這個則是局部描述符表.簡稱 LDT表.

  那么如果多個進程我們就需要多個描述符表.各自放在自己的低2G空間,高2G空間的描述符表是一樣.

那么此時就產生了冗余問題.高2G的描述符表都是一樣的,所以建立一個全局的描述符表. 簡稱GDT

LDT表只需要操作系統給表的地址即可.這些都是操作系統在實模式啟動的是否進行填寫的.

 

其中LDT = A進程,那么就執行A進程的操作.切換進程,並且保存進程的各種信息.以及各種表. 如果我們換成了B進程,那么就會切換到B進程.

如果我們手動切換,則是進行內核操作了,也就是所說的ROOTKIT,(內核補丁)的技術了.(我猜大部分學習內核的都是學習這種)

六丶段選擇子,進行查表.

什么是段選擇子.段選擇子是翻譯的.我們在32為的段選擇子是我們的段寄存器. 這些段寄存器都用來保護內存了.

段當下表進行查表動作.其中后2為是RPL,也是特權指令. 如果查表的是否RPL和描述符表的DPL一至則可以進行查表.不會出錯.  BLT2是表示查詢那個表, TL = 1表示是LDT表,TL = 0 表示GDT表.

3環下有讀取GDT和LDT描述符表的指令

 

SGDT [內存]  讀取SGDT表的起始位置到你指定的內存.  3環下可以讀不可以寫.  LGDT[內存] 寫,ring3不能執行.

其中如果在ring3下執行SGDT那么操作系統給你的是一個錯誤的值.

 

學習內核知識建議讀書 + 視頻. 資料只能簡單看看

課堂代碼資料:   鏈接:https://pan.baidu.com/s/1qZIt6hu 密碼:quao

轉載請注明出處: http://www.cnblogs.com/iBinary/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM