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