第 1 章 背景知識
1.1 Win32的軟硬件平臺
1.2 Windows的特色
1.3 必須了解的東西
1.3.1 80x86處理器的工作模式
??? 80386處理器有3種工作模式:實模式、保護模式和虛擬86模式。
??? 保護模式是80386處理器的主要工作模式。在此方式下,80386可以尋址4 GB的地址空間,同時,保護模式提供了8038的多任務(wù)、內(nèi)存分頁管理和優(yōu)先級保護等機制。
??? 虛擬86模式是為了在保護模式下提供和8086處理器的兼容,在虛擬86模式下,同樣支持任務(wù)切換、內(nèi)存分頁管理和優(yōu)先級,但內(nèi)存的尋址方式和8086相同,也是可以尋址1MB的空間。
??? 1. 實模式
??? 80386處理器被復(fù)位或加電的時候以實模式啟動。80386處理器在實模式下的存儲器尋址方式和8086是一樣的,由段寄存器的內(nèi)容乘以16當做基地址,加上段內(nèi)的偏移地址形成最終的物理地址,這時候它的32位地址線只使用了低20位。在實模式下,80386處理器不能對內(nèi)存進行分頁管理,所以指令尋址的地址就是內(nèi)存中實際的物理地址。在實模式下,所有的段都是可以讀、寫和執(zhí)行的。實模式下80386不支持優(yōu)先級,所有的指令相當于工作在特權(quán)級(優(yōu)先級0),所以它可以執(zhí)行所有特權(quán)指令。
??? 2. 保護模式
??? 當80386工作在保護模式下的時候,它的所有功能都是可用的。這時80386所有的32根地址線都可供尋址,物理尋址空間高達4 GB。在保護模式下,支持內(nèi)存分頁機制,提供了對虛擬內(nèi)存的良好支持。在保護模式下,80386處理器還支持優(yōu)先級機制。優(yōu)先級分4個級別(0級~3級),操作系統(tǒng)運行在最高的優(yōu)先級0上,應(yīng)用程序則運行在比較低的級別上。
??? DOS操作系統(tǒng)運行于實模式下,而Windows操作系統(tǒng)運行于保護模式下。
??? 3. 虛擬86模式
??? 虛擬86模式是為了在保護模式下執(zhí)行8086程序而設(shè)置的。雖然80386處理器已經(jīng)提供了實模式來兼容8086程序,但這時8086程序?qū)嶋H上只是運行得快了一點,對CPU的資源還是獨占的。虛擬86模式是以任務(wù)形式在保護模式上執(zhí)行的,在80386上可以同時支持由多個真正的80386任務(wù)和虛擬86模式構(gòu)成的任務(wù)。在虛擬86模式下,80386支持任務(wù)切換和內(nèi)存分頁。在Windows操作系統(tǒng)中,有一部分程序?qū)iT用來管理虛擬86模式的任務(wù),稱為虛擬86管理程序。為了和8086程序的尋址方式兼容,虛擬86模式采用和8086一樣的尋址方式,即用段寄存器乘以16當做基址再配合偏移地址形成線性地址,尋址空間為1 MB。但顯然多個虛擬86任務(wù)不能同時使用同一位置的1 MB地址空間,否則會引起沖突。操作系統(tǒng)利用分頁機制將不同虛擬86任務(wù)的地址空間映射到不同的物理地址上去,這樣每個虛擬86任務(wù)看起來都認為自己在使用0~1 MB的地址空間。
1.3.2 Windows的內(nèi)存管理
??? 1. DOS操作系統(tǒng)的內(nèi)存安排
??? DOS操作系統(tǒng)運行于實模式中,由于8086處理器的尋址范圍只有1 MB,當時系統(tǒng)硬件使用的存儲器地址被安排在高端,地址是從A0000h(即640 KB)開始的384 KB中,其中有用于顯示的視頻緩沖區(qū)和BIOS的地址空間。而在內(nèi)存低端,安排了中斷向量表和BIOS數(shù)據(jù)區(qū);剩下從500h開始到A0000h總共不到640 KB的內(nèi)存是操作系統(tǒng)和應(yīng)用程序所能夠使用的;應(yīng)用程序不可能使用這640 KB以外的內(nèi)存。這就是著名的“640KB限制”。
??? 2. 80386的內(nèi)存尋址機制
??? 因為80386所有的通用寄存器都是32位的,232相當于4G,所以用任何一個通用寄存器來間接尋址,不必分段就已經(jīng)可以訪問到所有的內(nèi)存地址。這是不是說,在保護模式下,段寄存器就不再有用了呢?答案是否定的。雖然在尋址上不再有分段的限制問題,但在保護模式下,一個地址空間是否可以被寫入,可以被多少優(yōu)先級的代碼寫入,是不是允許執(zhí)行等涉及保護的問題就出來了。要解決這些問題,必須對一個地址空間定義一些安全上的屬性。段寄存器這時就派上了用途。但是涉及屬性和保護模式下段的其他參數(shù),要表示的信息太多了,要用64位長的數(shù)據(jù)才能表示。我們把這64位的屬性數(shù)據(jù)叫做段描述符(Segment Descriptor)。把所有段的段描述符順序放在內(nèi)存中的指定位置,組成一個段描述符表(Descriptor Table);而段寄存器中的16位用來做索引信息,指定這個段的屬性用段描述符表中的第幾個描述符來表示。這時,段寄存器中的信息不再是段地址了,而是段選擇器(Segment Selector)。可以通過它在段描述符表中“選擇”一個項目以得到段的全部信息。
??? 80386中引入了兩個新的寄存器來管理段描述符表。一個是48位的全局描述符表寄存器GDTR,一個是16位的局部描述符表寄存器LDTR。GDTR指向的描述符表為全局描述符表GDT(Global Descriptor Table)。它包含系統(tǒng)中所有任務(wù)都可用的段描述符,通常包含描述操作系統(tǒng)所使用的代碼段、數(shù)據(jù)段和堆棧段的描述符及各任務(wù)的LDT段等;全局描述符表只有一個。
??? LDTR則指向局部描述符表LDT(Local Descriptor Table)。80386處理器設(shè)計成每個任務(wù)都有一個獨立的LDT。它包含有每個任務(wù)私有的代碼段、數(shù)據(jù)段和堆棧段的描述符,也包含該任務(wù)所使用的一些門描述符,如任務(wù)門和調(diào)用門描述符等。
??? 3. 80386的內(nèi)存分頁機制
??? 80386處理器把4 KB大小的一塊內(nèi)存當做一“頁”內(nèi)存,每頁物理內(nèi)存可以根據(jù)“頁目錄”和“頁表”,隨意映射到不同的線性地址上。這樣,就可以將物理地址不連續(xù)的內(nèi)存的映射連到一起,在線性地址上視為連續(xù)。頁表可以指定一個頁面并不真正映射到物理內(nèi)存中。這樣,訪問這個頁的指令會引發(fā)頁異常錯誤。這時,處理器會自動轉(zhuǎn)移到頁異常處理程序中去。操作系統(tǒng)可以在異常處理程序中將硬盤上的虛擬內(nèi)存讀到內(nèi)存中并修改頁表重新映射,然后重新執(zhí)行引發(fā)異常的指令。這樣指令可以正常執(zhí)行下去。
??? 4. Windows的內(nèi)存安排
??? 如果把虛擬內(nèi)存暫時先視為物理內(nèi)存的一部分,從物理內(nèi)存的層次看,Windows操作系統(tǒng)和DOS一樣,也是所有的內(nèi)容共享內(nèi)存。但是從應(yīng)用程序代碼的層次看,也就是說從分頁映射后線性地址的層次看,內(nèi)存的安排卻不是這個樣子。因為Windows是一個分時的多任務(wù)操作系統(tǒng),CPU時間被分成一個個的時間片后分配給不同程序輪流使用,在一個程序的時間片中,和這個程序執(zhí)行無關(guān)的東西(如其他程序的代碼和數(shù)據(jù))并不需要映射到線性地址中去。在物理內(nèi)存中,操作系統(tǒng)和系統(tǒng)DLL的代碼需要供每個應(yīng)用程序調(diào)用,所以在所有的時間片中都必須被映射;用戶程序只在自己所屬的時間片內(nèi)被映射;而用戶DLL則有選擇地被映射。假設(shè)程序A和程序C都要用到xxx.dll,那么物理內(nèi)存中xxx.dll的代碼在圖中的時間片1和n中被映射,其他的時間片就不需要映射,當然,物理內(nèi)存中只需要一份xxx.dll的代碼。
??? ● 每個應(yīng)用程序都有自己的4 GB的尋址空間。該空間可存放操作系統(tǒng)、系統(tǒng)DLL和用戶DLL的代碼,它們之中有各種函數(shù)供應(yīng)用程序調(diào)用。再除去其他的一些空間,余下的是應(yīng)用程序的代碼、數(shù)據(jù)和可以分配的地址空間。
??? ● 不同應(yīng)用程序的線性地址空間是隔離的。雖然它們在物理內(nèi)存中同時存在,但在某個程序所屬的時間片中,其他應(yīng)用程序的代碼和數(shù)據(jù)沒有被映射到可尋址的線性地址中,所以是不可訪問的。從編程的角度看,程序可以使用4 GB的尋址空間,而且這個空間是“私有”的。
??? ● DLL程序沒有自己“私有”的空間。它們總是被映射到其他應(yīng)用程序的地址空間中,當做其他應(yīng)用程序的一部分運行。原因很簡單,如果它不和其他程序同屬一個地址空間,應(yīng)用程序該如何調(diào)用它呢?
??? 5. 從Win32匯編的角度看內(nèi)存尋址
??? Win32匯編中的內(nèi)存訪問為用戶程序的代碼段、數(shù)據(jù)段和堆棧段全部預(yù)定義好了段描述符。這些段的起始地址為0,限長為ffffffff,所以用它們可以直接尋址全部的4 GB地址空間。程序開始執(zhí)行的時候,CS,DS,ES和SS都已經(jīng)指向了正確的描述符,在整個程序的生命周期內(nèi),程序員不必改動這些段寄存器,也不必關(guān)心它們的值究竟是多少(實際上是想改也改不了)。
??? 1.3.3 Windows的特權(quán)保護
??? 1. 80386的中斷和異常
??? 8086或80386實模式下中斷和異常的處理過程:實模式下的中斷和異常服務(wù)程序地址存放在中斷向量表中。中斷向量表位于物理內(nèi)存00000h開始的400h字節(jié)中,共支持100h個中斷向量;每個中斷向量是一個xxxx:yyyy格式的地址,占用4字節(jié)。當發(fā)生n號異常或n號中斷,或者執(zhí)行到int n指令的時候,CPU首先到內(nèi)存n×4的地方取出服務(wù)程序的地址aaaa:bbbb;然后將標志寄存器、中斷時的CS和IP壓入堆棧,接著轉(zhuǎn)移到aaaa:bbbb處執(zhí)行;在服務(wù)程序最后遇到iret的時候,CPU從堆棧中恢復(fù)標志寄存器,然后取出CS和IP并返回。
??? 在保護模式下,中斷或異常處理往往從用戶代碼切換到操作系統(tǒng)代碼中執(zhí)行。由于保護模式下的代碼有優(yōu)先級之分,為了使高優(yōu)先級的代碼能夠安全地被低優(yōu)先級的代碼調(diào)用,保護模式下增加了“門”的概念。“門”指向某個優(yōu)先級高的程序所規(guī)定的入口點,所有優(yōu)先級低的程序調(diào)用優(yōu)先級高的程序只能通過門重定向,進入門所規(guī)定的入口點。保護模式下的中斷和異常等服務(wù)程序也要從“門”進入,80386的門分為中斷門、自陷門和任務(wù)門幾種。
??? 在保護模式下要表示一個中斷或異常服務(wù)程序的信息需要用8個字節(jié),包括門的種類以及xxxx:yyyyyyyy格式的入口地址等。這組信息叫做“中斷描述符”。保護模式下把所有的中斷描述符放在一起組成“中斷描述符表”IDT(Interrupt Descriptor Table)。IDT不再放在固定的地址00000h處,而是采用可編程設(shè)置的方式,支持的中斷數(shù)量也可以設(shè)置。為此80386處理器引入了一個新的48位寄存器IDTR。IDTR的高32位指定了IDT在內(nèi)存中的基址(線性地址),低16位指定了IDT的長度,相當于指定了可以支持的中斷數(shù)量。
??? 2. 80386的保護機制
??? 保護機制主要由下列幾方面組成:
??? ● 段的類型檢查——段的類型是由段描述符指定的,主要屬性有是否可執(zhí)行,是否可讀和是否可寫等。而CS,DS和SS等段選擇器是否能裝入某種類型的段描述符是有限制的。如不可執(zhí)行的段不能裝入CS;不可讀的段不能裝入DS與ES等數(shù)據(jù)段寄存器;不可寫的段不能裝入SS等。如果段類型檢查通不過,則處理器會產(chǎn)生一般性保護異常或堆棧異常。
??? ● 頁的類型檢查——除了可以在段級別上指定整個段是否可讀寫外,在頁表中也可以為每個頁指定是否可寫。對于特權(quán)級下的執(zhí)行代碼,所有的頁都是可寫的。但對于1,2和3級的代碼,還要根據(jù)頁表中的R/W項決定是否可寫,企圖對只讀的頁進行寫操作會產(chǎn)生頁異常。
??? ● 訪問數(shù)據(jù)時的級別檢查——優(yōu)先級低的代碼不能訪問優(yōu)先級高的數(shù)據(jù)段。80386的段描述符中有一個DPL域(描述符優(yōu)先級),表示這個段可以被訪問的最低優(yōu)先級。而段選擇器中含有RPL域(請求優(yōu)先級),表示當前執(zhí)行代碼的優(yōu)先級。只有DPL在數(shù)值上大于或等于RPL值的時候,該段才是可以訪問的,否則會產(chǎn)生一般性保護異常。
??? ● 控制轉(zhuǎn)移的檢查——在處理器中,有很多指令可以實現(xiàn)控制轉(zhuǎn)移,如jmp,call,ret,int和iret等指令。但優(yōu)先級低的代碼不能隨意轉(zhuǎn)移到優(yōu)先級高的代碼中,所以遇到這些指令的時候,處理器要檢查轉(zhuǎn)移的目的位置是否合法。
??? ● 指令集的檢查——有兩類指令可以影響保護機制。第一類是改變GDT,LDT,IDT以及控制寄存器等關(guān)鍵寄存器的指令,稱為特權(quán)指令;第二類是操作I/O端口的指令以及cli和sti等改變中斷允許的指令,稱為敏感指令。試想一下,如果用戶級程序可以用sti禁止一切中斷(包括時鐘中斷),那么整個系統(tǒng)就無法正常運行,所以這些指令的運行要受到限制。特權(quán)指令只能在優(yōu)先級0上才能運行,而敏感指令取決于eflags寄存器中的IOPL位。只有IOPL位表示的優(yōu)先級高于等于當前代碼段的優(yōu)先級時,指令才能執(zhí)行。
??? ● I/O操作的保護——I/O地址也是受保護的對象。因為通過I/O操作可以繞過系統(tǒng)對很多硬件進行控制。80386可以單獨為I/O空間提供保護,每個任務(wù)有個TSS(任務(wù)狀態(tài)段)來記錄任務(wù)切換的信息。TSS中有個I/O允許位圖,用來表示對應(yīng)的I/O端口是否可以操作。某個I/O地址在位圖中的對應(yīng)數(shù)據(jù)位為0則表示可以操作;如果為1則還要看eflags中的IOPL位,這時只有IOPL位表示的優(yōu)先級高于等于當前代碼段的優(yōu)先級,才允許訪問該I/O端口。
??? 3. Windows的保護機制