win32 008

Win32基礎知識5

 

讓編程改變世界

Change the world by program


 

Windows的內存安排

 

這節課我們需要理解三個概念:

每個應用程序都有自己的4 GB的尋址空間,就算這個程序只暫 1KB的內存;

不同應用程序的線性地址空間是隔離的,儘管他們在內存中是搞在一起;

時刻要記住,DLL程序是“小三”,因此它們沒有自己“私有”的空間。

 

本節課我們將圖文並茂地來進行原理層面的分析!

 

虛擬內存安排:

 

Windows 系統一般在硬盤上建立大小爲物理內存兩倍左右的交換文件用作虛擬內存。

利用 80386處理器的內存分頁機制,交換文件的尋址上可以很方便地作爲物理內存使用。

(只需要在真正調用的時候將其讀入物理內存並同時修改線性地址映射到這塊內存即可)。

同樣道理,反正是映射一個地址而已,所以被執行的程序也可以不必裝入內存,只需要在頁表中建立映射關係,真正運行到這段代碼才調入內存。

 

衆所周知,Windows 是一個分時的多任務操作系統,CPU時間(就是CPU運行的過程)被分成一個個的時間片後分配給不同程序輪流使用。

在A程序的時間片中,和這個程序執行無關的部分(B和C等其他程序的代碼和數據)並不需要映射到線性地址中。

 

附加解析:

內存中,所有的程序都搞在一起,關係十分混亂;

CPU只能看到線性地址(假的),每個程序擁有自己的線性地址(小三除外)。

 

虛擬內存安排

 

總結:WIN32編程中幾個很重要的概念

 

第一點:

每個應用程序都有自己的4 GB的尋址空間。

該空間可存放操作系統、系統DLL和用戶DLL的代碼,它們之中有各種函數供應用程序調用。

再除去其他的一些空間,餘下的是應用程序的代碼、數據和可以分配的地址空間。

 

第二點:

不同應用程序的線性地址空間是隔離的。

雖然它們在物理內存中同時存在,但在某個程序所屬的時間片中,其他應用程序的代碼和數據沒有被映射到可尋址的線性地址中,所以是不可訪問的。

從編程的角度看,程序可以使用4 GB的尋址空間,而且這個空間是“私有”的。

 

第三點:

DLL程序沒有自己“私有”的空間。

它們總是被映射到其他應用程序的地址空間中,當做其他應用程序的一部分運行。

原因很簡單,如果它不和其他程序同屬一個地址空間,應用程序該如何調用它呢?

 

從WIN32彙編的角度看內存尋址

 

如果堅持不到這句話出現的同學就因爲前邊Windows原理太複雜而放棄的童鞋,小甲魚覺得很可惜……

Win32彙編中的內存訪問遠比DOS下的分段尋址方式簡單,這是爲什麼呢?

因爲Windows是一個多任務的操作系統,最首要的宗旨就是“穩定壓倒一切”。

 

如果把描述符表以及頁表等內容交給用戶程序管理是很不安全的。

任何權限上開放引發的安全問題都是很嚴重的,如Windows 9x中的中斷描述符表是可寫的,CIH病毒可利用它將自己的權限提高到優先級0;

而Windows NT下的中斷描述符表是不可寫的,CIH病毒在Windows NT下就無法使用同樣的方法進駐內存。

 

關於CIH病毒可以到魚C論壇“資源分享”版塊下載^_^

 

正因爲如此,Windows操作系統乾脆爲用戶程序“安排好了一切”。

具體表現在爲用戶程序的代碼段、數據段和堆棧段全部預定義好了段描述符。這些段的起始地址爲0,限長爲ffffffff,所以用它們可以直接尋址全部的4 GB地址空間。

 

程序開始執行的時候,CS,DS,ES和SS都已經指向了正確的描述符,在整個程序的生命週期內,程序員不必改動這些段寄存器,也不必關心它們的值究竟是多少(實際上是想改也改不了)。

所以對Win32彙編程序來說,整個源程序中竟然可以不用出現段寄存器的身影。

這在DOS彙編編程中是不可想像的。

 

回顧之前提出的問題:

“爲什麼在WIN32彙編源代碼中看不到CS,DS,ES,SS等段寄存器的使用?”

答案是:並不是Win32彙編源代碼用不到段寄存器,而是用戶在使用中不必去關心段寄存器!

Win32基礎知識6

 

讓編程改變世界

Change the world by program


 

Windows的特權保護

 

Windows 的特權保護和處理器硬件的支持是分不開的。

優先級的劃分、指令的權限檢查和超出權限訪問的異常處理等是構成特權保護的基礎。

 

這一講我們將試圖講過講解爲大家解決兩大問題:

Win32彙編中爲什麼找不到中斷指令的應用?

Windows錯誤的“藍屏”是從哪裏來的?

 

小甲魚解釋中斷和異常

 

魚C故事會:

假設某一天你正在興致勃勃、興高采烈地看一部愛情動作片,但是突然媽媽在外邊猛敲你的房門,因爲她發覺家裏的醬油沒了……

這時候沒辦法,還是老媽的命令重要,因此,我們暫停了視頻,然後去打醬油……

打完醬油回來,你又接着往下看,意興珊闌處,停電了……

過了N久,好不容易來電了,一開機,它藍屏了!

 

官方解釋 — 什麼是中斷

中斷指當程序執行過程中有更重要的事情需要實時處理時(如串口中有數據到達,不及時處理數據會丟失,串行控制器就提交一箇中斷信號給處理器要求處理),硬件通過中斷控制器通知處理器。

接到命令後,處理器暫時掛起當前運行的程序,轉移到中斷處理程序中。

當中斷處理程序處理完畢後,通過iret指令回到原先被打斷的程序中繼續執行。

 

官方解釋 — 什麼是異常

異常指指令執行中發生不可忽略的錯誤時(如遇到無效的指令編碼,除法指令除零等),處理器用和中斷處理相同的操作方法掛起當前運行的程序轉移到異常處理程序中。

異常處理程序決定在修正錯誤後是否回到原來的地方繼續執行。

 

注意:中斷和異常處理的方式是相同的!!

 

實模式下的中斷或異常處理:

實模式下的中斷和異常服務程序地址存放在中斷向量表中。

中斷向量表位於物理內存中,每個中斷向量是一個xxxx:yyyy格式的地址,佔用4字節。

當發生n號異常或n號中斷,或者執行到int n指令的時候,CPU首先到內存n×4的地方取出服務程序的地址aaaa:bbbb

然後將標誌寄存器、中斷時的CS和IP壓入堆棧,接着轉移到aaaa:bbbb處執行

 

保護模式下的中斷或異常處理

 

保護模式下,中斷或異常處理往往從用戶代碼切換到操作系統代碼中執行。

由於保護模式下的代碼有優先級之分,因此出現了從優先級低的應用程序轉移到優先級高的系統代碼中的問題。

 

如果優先級低的代碼能夠任意調用優先級高的代碼,就相當於擁有了高優先級代碼的權限。

爲了使高優先級的代碼能夠安全地被低優先級的代碼調用,保護模式下增加了“門”的概念。

 

“門”指向某個優先級高的程序所規定的入口點,所有優先級低的程序調用優先級高的程序只能通過門重定向,進入門所規定的入口點。

這樣可以避免低級別的程序代碼從任意位置進入優先級高的程序的問題。

保護模式下的中斷和異常等服務程序也要從“門”進入,80386的門分爲中斷門、自陷門和任務門幾種。

 

保護模式下中斷或異常示意圖

保護模式下中斷或異常示意圖

保護模式下把所有的中斷描述符放在一起組成”中斷描述符表IDT”。

爲此80386處理器引入了一個新的48位寄存器IDTR。

 

IDTR的高32位指定了IDT在內存中的基址(線性地址),低16位指定了IDT的長度,相當於指定了可以支持的中斷數量。

保護模式下發生異常或中斷時,處理器先根據IDTR寄存器得到中斷描述符的地址,然後取出n號中斷/異常的門描述符,再從描述符中得到中斷服務程序的地址xxxx:yyyyyyyy,經過段地址轉換後得到服務程序的32位線性地址並轉移後執行。

 

在Windows中,操作系統使用動態鏈接庫來代替中斷服務程序提供系統功能,所以 Win32彙編中int指令也就失去了存在的意義。

這就是在Win32彙編源代碼中看不到int指令的原因。

其實那些調用API的指令原本是用int指令實現的。

 

80386的保護機制

 

80286之前的處理器只支持單任務,操作系統並沒有什麼安全性可言,計算機的全部資源包括操作系統的內部資源都可以任憑程序員調用。

但對於多任務的操作系統,某個想搗亂的程序爲所欲爲令使所有程序都無法運行。

 

所以80286及以上的處理器引入了優先級的概念。80386處理器共設置4個優先級(0~3):

0級是最高級(特權級)3級是最低級(用戶級)

 

保護機制主要由下列幾方面組成

 

段的類型檢查

段的類型是由段描述符指定的,主要屬性有是否可執行,是否可讀和是否可寫等。

CS,DS和SS等段選擇器是否能裝入某種類型的段描述符是有限制的。

如不可執行的段不能裝入CS;不可讀的段不能裝入DS與ES等數據段寄存器;不可寫的段不能裝入SS等。

如果段類型檢查通不過,則處理器會產生一般性保護異常或堆棧異常。

 

頁的類型檢查

除了可以在段級別上指定整個段是否可讀寫外,在頁表中也可以爲每個頁指定是否可寫。

對於特權級下的執行代碼,所有的頁都是可寫的。

但對於1,2和3級的代碼,還要根據頁表中的R/W項決定是否可寫,企圖對只讀的頁進行寫操作會產生頁異常。

 

訪問數據時的級別檢查

優先級低的代碼不能訪問優先級高的數據段。80386的段描述符中有一個DPL域(描述符優先級),表示這個段可以被訪問的最低優先級。

而段選擇器中含有RPL域(請求優先級),表示當前執行代碼的優先級。

只有DPL在數值上大於或等於RPL值的時候,該段纔是可以訪問的,否則會產生一般性保護異常。

 

控制轉移的檢查

在處理器中,有很多指令可以實現控制轉移,如jmp,call,ret,int和iret等指令。

但優先級低的代碼不能隨意轉移到優先級高的代碼中,所以遇到這些指令的時候,處理器要檢查轉移的目的位置是否合法。

 

指令集的檢查

有兩類指令可以影響保護機制。

第一類是改變GDT,LDT,IDT以及控制寄存器等關鍵寄存器的指令,稱爲特權指令

第二類是操作I/O端口的指令以及cli和sti等改變中斷允許的指令,稱爲敏感指令。

特權指令只能在優先級0上才能運行,而敏感指令取決於eflags寄存器中的IOPL位。

只有IOPL位表示的優先級高於等於當前代碼段的優先級時,指令才能執行。

 

I/O操作的保護

I/O地址也是受保護的對象。因爲通過I/O操作可以繞過系統對很多硬件進行控制。

80386可以單獨爲I/O空間提供保護,每個任務有個TSS(任務狀態段)來記錄任務切換的信息。

TSS中有個I/O允許位圖,用來表示對應的I/O端口是否可以操作。

 

Windows的保護機制

以上是Windows規定的“保護條例”,如果某個程序違反了,那麼會引發保護異常,處理器會毫不猶豫地把控制權轉移到對應的異常處理程序中去。

Windows會在處理程序中用一個很酷的“非法操作”對話框把用戶的程序判死刑,沒有一點回旋的餘地!

經常時候系統會用一個“藍屏”來通知用戶程序試圖訪問不存在的內存頁。

 

小結

至此,本章節的講解到終於到一段落。

小甲魚此時此刻的心情出了哀怨作者外剩下的就是釋然。

因爲本章節可以說是全書最難懂、最讓人抓狂的一章節,相信從第二章開始,我們的學習會變得格外輕鬆和愉快!

加油!!魚C教學 — 讓編程學習充滿歡樂!

發佈了49 篇原創文章 · 獲贊 18 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章