版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第二章平?臺(tái)?與?工?具2.1硬件平臺(tái)2.2Intel處理器體系結(jié)構(gòu)2.3GNUC語(yǔ)言2.4GNU匯編語(yǔ)言2.5GNU鏈接腳本2.6常用數(shù)據(jù)結(jié)構(gòu)如果不考慮虛擬機(jī)監(jiān)控器(VMM),操作系統(tǒng)內(nèi)核就是最底層的系統(tǒng)軟件。操作系統(tǒng)內(nèi)核直接運(yùn)行在計(jì)算機(jī)硬件平臺(tái)之上,其設(shè)計(jì)技術(shù)與實(shí)現(xiàn)方法都與硬件平臺(tái)有著十分密切的關(guān)系。離開(kāi)了硬件平臺(tái)的支持,操作系統(tǒng)內(nèi)核的許多管理工作都難以開(kāi)展。事實(shí)上,計(jì)算機(jī)硬件平臺(tái)中的許多功能也是專(zhuān)門(mén)為操作系統(tǒng)內(nèi)核設(shè)計(jì)的,只有操作系統(tǒng)內(nèi)核才會(huì)使用它們。要了解操作系統(tǒng)內(nèi)核的原理與結(jié)構(gòu),就必須了解計(jì)算機(jī)的硬件平臺(tái)。
在復(fù)雜的計(jì)算機(jī)硬件平臺(tái)中,最核心的是處理器,與內(nèi)核設(shè)計(jì)關(guān)系最密切的也是處理器。雖然Linux內(nèi)核可以運(yùn)行在多種處理器之上,但I(xiàn)ntel系列的處理器是Linux支持的第一種處理器,也是目前最常見(jiàn)的處理器,更是本書(shū)的討論基礎(chǔ)。
Linux內(nèi)核是用C和匯編語(yǔ)言寫(xiě)成的,然而它所用的C語(yǔ)言經(jīng)過(guò)了GNU的擴(kuò)展,所用的匯編語(yǔ)言采用的是AT&T的格式。Linux內(nèi)核的實(shí)現(xiàn)充分利用了GNUC和AT&T匯編的擴(kuò)展特性,與這兩種語(yǔ)言的結(jié)合極為緊密。GNUC和AT&T格式的匯編是Linux的核心開(kāi)發(fā)工具,也是理解Linux內(nèi)核源代碼的基礎(chǔ)。
另外,在Linux內(nèi)核的諸多數(shù)據(jù)結(jié)構(gòu)中,最常見(jiàn)的是鏈表和樹(shù)。鏈表和樹(shù)的實(shí)現(xiàn)方式很多,為了避免重復(fù),Linux設(shè)計(jì)了通用鏈表和紅黑樹(shù)。當(dāng)需要將某種結(jié)構(gòu)組織成鏈表或紅黑樹(shù)時(shí),Linux就會(huì)在其中嵌入一個(gè)通用鏈表節(jié)點(diǎn)或紅黑樹(shù)節(jié)點(diǎn)。
操作系統(tǒng)所管理的計(jì)算機(jī)硬件平臺(tái)大致由CPU、內(nèi)存、外存和其它外部設(shè)備組成,它們之間通過(guò)總線(xiàn)連接在一起。圖2.1是一種抽象的計(jì)算機(jī)硬件平臺(tái)的組織結(jié)構(gòu)。2.1硬件平臺(tái)
圖2.1計(jì)算機(jī)硬件平臺(tái)的組織結(jié)構(gòu)處理器又叫CPU,是整個(gè)計(jì)算機(jī)系統(tǒng)的大腦,它負(fù)責(zé)執(zhí)行由指令構(gòu)成的程序,并通過(guò)程序的執(zhí)行來(lái)控制整個(gè)計(jì)算機(jī)系統(tǒng)。一個(gè)計(jì)算機(jī)系統(tǒng)中可以有一個(gè)或多個(gè)處理器,一個(gè)處理器中又可以有一個(gè)或多個(gè)核(Core)。為方便起見(jiàn),可以將一個(gè)核看成一個(gè)獨(dú)立的處理器。一個(gè)以多核處理器為核心的計(jì)算機(jī)系統(tǒng)等價(jià)于一個(gè)多處理器(SMP)系統(tǒng)。
內(nèi)存是處理器執(zhí)行程序、加工數(shù)據(jù)的場(chǎng)所,是處理器可以直接訪(fǎng)問(wèn)的存儲(chǔ)空間。內(nèi)存通常被抽象成一個(gè)字節(jié)數(shù)組,其中的每個(gè)字節(jié)都有一個(gè)地址。處理器可通過(guò)地址隨機(jī)地訪(fǎng)問(wèn)內(nèi)存中的任意一個(gè)字節(jié)。為了加快內(nèi)存的訪(fǎng)問(wèn)速度,計(jì)算機(jī)系統(tǒng)中通常都提供了一些高速緩存(Cache)。Cache通常由硬件管理。
I/O設(shè)備通常由I/O控制器和物理設(shè)備組成。處理器通過(guò)I/O控制器管理物理設(shè)備。對(duì)內(nèi)核來(lái)說(shuō),I/O控制器主要由控制與狀態(tài)寄存器(CSR)和數(shù)據(jù)寄存器組成。處理器通過(guò)讀CSR獲得設(shè)備的狀態(tài)、通過(guò)寫(xiě)CSR來(lái)控制設(shè)備的動(dòng)作、通過(guò)讀寫(xiě)數(shù)據(jù)寄存器與I/O設(shè)備交換數(shù)據(jù)。因而,內(nèi)核通常將一個(gè)I/O設(shè)備抽象成一組寄存器,并給每個(gè)寄存器一個(gè)I/O地址。處理器通過(guò)I/O地址訪(fǎng)問(wèn)所有的I/O寄存器。有些處理器還提供了I/O指令,專(zhuān)門(mén)用于訪(fǎng)問(wèn)I/O寄存器,如Intel的in、out指令?,F(xiàn)代計(jì)算機(jī)系統(tǒng)中的許多設(shè)備寄存器可被映射到物理地址空間中。此時(shí),每個(gè)設(shè)備寄存器都有一個(gè)物理內(nèi)存地址,處理器可以像訪(fǎng)問(wèn)物理內(nèi)存一樣訪(fǎng)問(wèn)設(shè)備的寄存器。這種方式的I/O稱(chēng)為內(nèi)存映射I/O(MMIO),它的使用更加方便,但會(huì)消耗物理地址。
在所有的I/O設(shè)備中,對(duì)系統(tǒng)影響最大的是外部存儲(chǔ)設(shè)備,如硬盤(pán)、光盤(pán)等。操作系統(tǒng)、應(yīng)用程序、數(shù)據(jù)文件等都存儲(chǔ)在外部存儲(chǔ)設(shè)備(如磁盤(pán))上。為了便于管理,通常把外存抽象成一個(gè)數(shù)據(jù)塊的數(shù)組,每個(gè)數(shù)據(jù)塊都有一個(gè)序號(hào)。處理器可以通過(guò)序號(hào)隨機(jī)地讀、寫(xiě)外存中的任何一個(gè)數(shù)據(jù)塊。對(duì)外存的操作以塊為單位,因此又稱(chēng)外存為塊設(shè)備。對(duì)應(yīng)地,其它I/O設(shè)備稱(chēng)為字符設(shè)備。
總線(xiàn)負(fù)責(zé)將處理器、內(nèi)存、I/O控制器等連接起來(lái),組合成一個(gè)完整的計(jì)算機(jī)系統(tǒng)。常用的總線(xiàn)有ISA、PCI、PCI-E、AGP、ATA、SCSI等??偩€(xiàn)除負(fù)責(zé)計(jì)算機(jī)系統(tǒng)中各部件之間的通信之外,還負(fù)責(zé)檢測(cè)、枚舉連接在其上的設(shè)備,報(bào)告它們的信息。
在眾多的處理器中,最常見(jiàn)的是Intel處理器。Intel處理器是一個(gè)大家族,包括多個(gè)系列的產(chǎn)品,如80386、80486、Pentium、PentiumII、PentiumIII、Pentium4、Xeon、CoreTMDuo、CoreTMSolo等。若按處理器的體系結(jié)構(gòu)劃分,可將主流的Intel處理器分為兩大類(lèi),即IA-32和Intel64。其中,IA-32提供32位編程環(huán)境,Intel64提供64位編程環(huán)境。Intel64與IA-32是兼容的。2.2Intel處理器體系結(jié)構(gòu)
Intel處理器為操作系統(tǒng)內(nèi)核的設(shè)計(jì)提供了多種支持機(jī)制,包括操作模式、內(nèi)存管理機(jī)制、進(jìn)程管理機(jī)制、中斷處理機(jī)制、保護(hù)機(jī)制、專(zhuān)用寄存器和指令等。2.2.1處理器操作模式
定義操作模式的目的主要是為了兼容。在設(shè)計(jì)8086處理器時(shí),Intel并沒(méi)有定義操作模式,此時(shí)的處理器使用20位的物理地址,最多可訪(fǎng)問(wèn)1MB的物理內(nèi)存,也未對(duì)操作系統(tǒng)進(jìn)行任何保護(hù)。當(dāng)80386出現(xiàn)時(shí),處理器開(kāi)始使用32位的邏輯地址,而且提供了程序間的隔離與保護(hù),于是引入了保護(hù)模式以區(qū)分前期的實(shí)模式。為了在保護(hù)模式中能夠運(yùn)行實(shí)模式的程序,又引入了虛擬8086模式。當(dāng)Intel64出現(xiàn)之后,處理器開(kāi)始使用64位地址,于是又引入了64位模式以區(qū)別于前期的保護(hù)模式。為了在64位模式中運(yùn)行保護(hù)模式的程序,又引入了兼容模式。
IA-32體系結(jié)構(gòu)提供了3種操作模式和1種準(zhǔn)操作模式。實(shí)模式是與8086兼容的操作模式,但有一些擴(kuò)展。保護(hù)模式是處理器的一種最基本的操作模式,在這種模式中,處理器的所有指令以及體系結(jié)構(gòu)的所有特色都是可用的,并且能夠達(dá)到最高的性能。系統(tǒng)管理模式是一種特殊的操作模式,是提供給操作系統(tǒng)的一種透明管理機(jī)制,用于實(shí)現(xiàn)電源管理等特殊操作。虛擬8086模式是一個(gè)準(zhǔn)操作模式,允許處理器在保護(hù)模式中執(zhí)行實(shí)模式的程序。
Intel64體系結(jié)構(gòu)又新增了一種IA-32e操作模式,該操作模式又包含兩種子模式,即兼容模式和64位模式。當(dāng)處理器運(yùn)行在兼容模式時(shí),它可以不加修改地運(yùn)行大多數(shù)IA-32體系結(jié)構(gòu)的程序。當(dāng)處理器運(yùn)行在64位模式時(shí),它可以使用64位的線(xiàn)性地址空間和一些新增加的特性。IA-32e不再支持虛擬8086模式。
處理器加電或Reset后的默認(rèn)操作模式是實(shí)模式。操作系統(tǒng)內(nèi)核的初始化部分負(fù)責(zé)將處理器由實(shí)模式切換到其它模式。實(shí)模式和保護(hù)模式之間的轉(zhuǎn)換由控制寄存器CR0中的PE位控制;保護(hù)模式與IA-32e模式之間的轉(zhuǎn)換由IA32_EFER寄存器中的LME和CR0中的PG位控制;兼容模式與64位模式之間的轉(zhuǎn)換由代碼段寄存器CS中的L位控制;保護(hù)模式和虛擬8086模式之間的轉(zhuǎn)換由標(biāo)志寄存器EFLAGS中的VM位控制。
進(jìn)入系統(tǒng)管理模式的唯一途徑是SMI中斷。在系統(tǒng)管理模式中執(zhí)行指令RSM會(huì)將處理器切換回原來(lái)的操作模式。操作模式之間的轉(zhuǎn)換關(guān)系如圖2.2所示。
圖2.2處理器操作模式之間的轉(zhuǎn)換2.2.2段頁(yè)式內(nèi)存管理
IA-32體系結(jié)構(gòu)提供了極為復(fù)雜的段頁(yè)式內(nèi)存管理機(jī)制,即先分段,再分頁(yè)。其中段式管理是默認(rèn)、必須的,頁(yè)式管理是可選的。保留段式是為了兼容,提供頁(yè)式是為了支持虛擬內(nèi)存。
在段式管理中,處理器可尋址的線(xiàn)性?xún)?nèi)存空間被劃分成了若干個(gè)大小不等的段。一個(gè)段就是線(xiàn)性地址空間中的一個(gè)連續(xù)的區(qū)間。段中可保存代碼、數(shù)據(jù)、堆?;蚱渌到y(tǒng)級(jí)的數(shù)據(jù)結(jié)構(gòu)。段的屬性信息由與之對(duì)應(yīng)的段描述符描述。段描述符是一個(gè)數(shù)據(jù)結(jié)構(gòu),其一般格式如圖2.3所示。
圖2.3段描述符結(jié)構(gòu)描述符中的第2、3、4、7字節(jié)組成了段的基地址(Baseaddress),用于定義段在線(xiàn)性地址空間中的開(kāi)始位置?;刂房梢栽?~4GB之間浮動(dòng)。
描述符中的第0、1字節(jié)和6字節(jié)的低4位(共20位)組成了段界限(Segmentlimit),用于定義段的長(zhǎng)度。長(zhǎng)度的單位由粒度(G)位表示。當(dāng)G為0時(shí),段以字節(jié)為單位,最大段長(zhǎng)為1MB;當(dāng)G為1時(shí),段以頁(yè)(4KB)為單位,最大段長(zhǎng)為4GB。
DPL是段的特權(quán)級(jí),其值在0~3之間。
S是系統(tǒng)標(biāo)志,用于區(qū)分段的類(lèi)別,0表示系統(tǒng)段,1表示用戶(hù)段。
Type是段的類(lèi)型。對(duì)系統(tǒng)段,類(lèi)型域由4位組成,可表示16個(gè)系統(tǒng)段類(lèi)型之一,如2表示LDT、9表示32位有效TSS、B表示32位忙TSS、E表示中斷門(mén)、F表示陷阱門(mén)等。對(duì)用戶(hù)段,類(lèi)型域中的4位(0~3,3為高位)被重新解釋如下:
(1)第3位為0表示數(shù)據(jù)段。此時(shí),第2位表示地址擴(kuò)展方向(0表示向大方向擴(kuò)展,1表示向小方向擴(kuò)展),第1位表示段是否可寫(xiě)(0表示不能寫(xiě),1表示可寫(xiě))。
(2)第3位為1表示代碼段。此時(shí),第2位是相容標(biāo)志(0表示非相容,1表示相容),第1位是可讀位(表示代碼段是否允許讀,1表示允許,0表示不允許)。保護(hù)模式的代碼段不允許寫(xiě)。
(3)第0位是存取位,0表示段尚未被存取過(guò),1表示段已被存取過(guò)。
D/B標(biāo)志表示有效地址和操作數(shù)的長(zhǎng)度。
L標(biāo)志僅出現(xiàn)在IA-32e模式的代碼段描述符中,用于表示執(zhí)行該段代碼時(shí)處理器的操作子模式,1表示64位模式,0表示兼容模式。
堆棧段通常是向下擴(kuò)展的、可讀寫(xiě)的數(shù)據(jù)段。
按照Intel的設(shè)想,每個(gè)進(jìn)程(Intel稱(chēng)任務(wù))都可以定義自己的代碼段、數(shù)據(jù)段等,而每個(gè)段都需要描述符,因而系統(tǒng)中會(huì)有許多描述符。為了便于管理,Intel用段描述符表來(lái)組織系統(tǒng)中的描述符。段描述符表是一個(gè)段描述符的數(shù)組,大小可變,最大可達(dá)64KB,最多可保存8192個(gè)8字節(jié)的段描述符。段描述符表又分為兩大類(lèi),即全局描述符表和局部描述符表。全局描述符表(GDT)中的描述符是全局共用的,其中的第0個(gè)描述符保留不用(全為0)。在系統(tǒng)進(jìn)入保護(hù)模式之前必須為其定義一個(gè)GDT。由于GDT本身僅是線(xiàn)性地址空間中的一個(gè)數(shù)據(jù)結(jié)構(gòu),沒(méi)有對(duì)應(yīng)的描述符,因而IA-32體系結(jié)構(gòu)專(zhuān)門(mén)定義了GDTR寄存器來(lái)存放當(dāng)前GDT的信息。
與GDT不同,局部描述符表(LDT)是系統(tǒng)段,其中可存放局部的段描述符,如進(jìn)程自己的代碼段、數(shù)據(jù)段等。定義LDT的描述符叫LDT描述符,出現(xiàn)在GDT中。IA-32體系結(jié)構(gòu)專(zhuān)門(mén)提供了一個(gè)LDTR寄存器,用于保存當(dāng)前使用的LDT的信息。有了描述符表之后,可以用描述符在GDT或LDT中的索引來(lái)標(biāo)識(shí)它,這種標(biāo)識(shí)稱(chēng)為段選擇符。段選擇符是16位的標(biāo)識(shí)符,它的第3~15位是索引,表示描述符在GDT或LDT中的位置;第2位是指示器(TI),表示索引所對(duì)應(yīng)的描述符表(0表示GDT,1表示LDT);第0、1兩位是請(qǐng)求特權(quán)級(jí)RPL。
一個(gè)段選擇符加上一個(gè)偏移量可以唯一地標(biāo)識(shí)一個(gè)邏輯地址。邏輯地址是程序中使用的地址,不是線(xiàn)性地址,也不是物理地址,在使用之前必須對(duì)其進(jìn)行轉(zhuǎn)換。轉(zhuǎn)換的過(guò)程是:以段選擇符為索引查描述符表,獲得段的描述符,從中取出段的基地址,將其加上偏移量即可得到與之對(duì)應(yīng)的線(xiàn)性地址,如圖2.4所示。圖2.4邏輯地址到線(xiàn)性地址的轉(zhuǎn)換如果沒(méi)有啟動(dòng)分頁(yè)機(jī)制,經(jīng)過(guò)段描述符轉(zhuǎn)換后的線(xiàn)性地址就是物理地址。
邏輯地址的轉(zhuǎn)換極為頻繁,應(yīng)該將最近使用的描述符緩存起來(lái),以加快地址轉(zhuǎn)換的速度。為此,IA-32體系結(jié)構(gòu)提供了6個(gè)段寄存器,即CS、SS、DS、ES、FS和GS,每個(gè)段寄存器中可以緩存一個(gè)段描述符。有了段寄存器后,在使用一個(gè)段之前,可以先將它的描述符裝入到一個(gè)段寄存器中。如此以來(lái),邏輯地址就變成了段寄存器+偏移量,邏輯地址到線(xiàn)性地址的轉(zhuǎn)換可以在處理器中完成,不需要再查描述符表。雖然以段為單位可以進(jìn)行內(nèi)存管理,但這種方法比較笨拙,而且與現(xiàn)代虛擬內(nèi)存管理的思想不符,因而IA-32體系結(jié)構(gòu)允許對(duì)段內(nèi)的內(nèi)存進(jìn)行再分頁(yè),即在段式管理的基礎(chǔ)上再增加一套頁(yè)式管理,也就是段頁(yè)式管理。
在段頁(yè)式管理中,段內(nèi)的線(xiàn)性地址空間被分割成大小相等的線(xiàn)性頁(yè)(4KB、
4MB或2MB等),物理內(nèi)存空間也被分成同樣大小的物理頁(yè)。操作系統(tǒng)內(nèi)核維護(hù)一個(gè)頁(yè)表,用于管理線(xiàn)性頁(yè)到物理頁(yè)的映射。在頁(yè)表中,一個(gè)線(xiàn)性頁(yè)可以有對(duì)應(yīng)的物理頁(yè),此時(shí)利用頁(yè)表可以完成線(xiàn)性地址到物理地址的轉(zhuǎn)換。一個(gè)線(xiàn)性頁(yè)也可以沒(méi)有對(duì)應(yīng)的物理頁(yè),此時(shí)的線(xiàn)性地址無(wú)法直接轉(zhuǎn)換成物理地址,處理器會(huì)產(chǎn)生一個(gè)fault異常。操作系統(tǒng)內(nèi)核在處理這種異常時(shí),可以臨時(shí)為線(xiàn)性頁(yè)分配物理頁(yè)并在其中填入適當(dāng)?shù)膬?nèi)容。異常處理之后,線(xiàn)性地址即可以轉(zhuǎn)換成物理地址。
頁(yè)表可能很大,因而又被分成多級(jí),如IA-32體系結(jié)構(gòu)將它的頁(yè)表分成兩級(jí),即頁(yè)目錄和頁(yè)表。頁(yè)目錄是一個(gè)數(shù)組,其元素叫頁(yè)目錄項(xiàng)(PDE),每個(gè)頁(yè)目錄項(xiàng)描述一個(gè)頁(yè)表。頁(yè)目錄的大小為一頁(yè)(4KB),頁(yè)目錄項(xiàng)的大小為4字節(jié),所以一個(gè)頁(yè)目錄中有1024個(gè)頁(yè)目錄項(xiàng),最多可描述1024個(gè)頁(yè)表。頁(yè)表也是一個(gè)數(shù)組,其元素叫頁(yè)表項(xiàng)(PTE),每個(gè)頁(yè)表項(xiàng)描述一個(gè)線(xiàn)性頁(yè)。頁(yè)表大小為一頁(yè)(4KB),頁(yè)表項(xiàng)的大小為4字節(jié),所以一個(gè)頁(yè)表最多可描述1024個(gè)線(xiàn)性頁(yè)。
由于物理頁(yè)是預(yù)先劃分好的,其開(kāi)始位置一定在4KB的邊界上,因而頁(yè)目錄和頁(yè)表項(xiàng)的低12位肯定是0,可以用它們存儲(chǔ)頁(yè)表或頁(yè)的管理控制信息,如是否有對(duì)應(yīng)的物理頁(yè)、是否可寫(xiě)等。頁(yè)目錄項(xiàng)與頁(yè)表項(xiàng)的結(jié)構(gòu)基本相同。圖2.5是頁(yè)表項(xiàng)的結(jié)構(gòu)。圖2.5頁(yè)表項(xiàng)的結(jié)構(gòu)在頁(yè)表項(xiàng)結(jié)構(gòu)中,P是存在位,表示它所描述的頁(yè)表或頁(yè)目前是否在物理內(nèi)存中,1表示在內(nèi)存,0表示不在內(nèi)存;R/W是讀寫(xiě)標(biāo)志位,表示頁(yè)表或頁(yè)是否允許寫(xiě),0表示只讀,1表示可讀可寫(xiě);U/S是用戶(hù)標(biāo)志位,表示頁(yè)表或頁(yè)的特權(quán)級(jí),0表示超級(jí)用戶(hù)(特權(quán)級(jí)為0、1、2),1表示普通用戶(hù)(特權(quán)級(jí)為3);A是存取標(biāo)志位,表示頁(yè)表或頁(yè)有沒(méi)有被存取(讀、寫(xiě))過(guò),當(dāng)頁(yè)表或頁(yè)被存取時(shí),處理器自動(dòng)設(shè)置該標(biāo)志;D是臟標(biāo)志位,表示頁(yè)是否被修改過(guò),當(dāng)頁(yè)被修改時(shí),處理器自動(dòng)設(shè)置該標(biāo)志。
在頁(yè)目錄項(xiàng)中,PAT標(biāo)志變成了PS標(biāo)志,表示物理頁(yè)的尺寸。線(xiàn)性地址到物理地址轉(zhuǎn)換的過(guò)程是:按照頁(yè)表層次將線(xiàn)性地址劃分成多個(gè)片段;從最高片段開(kāi)始,以片段值為索引逐個(gè)查對(duì)應(yīng)的頁(yè)表,獲得下一級(jí)頁(yè)表的位置;查最后一級(jí)頁(yè)表,獲得物理頁(yè)的位置;將物理頁(yè)的開(kāi)始地址加上最后一個(gè)片段的值(偏移量)得到的就是線(xiàn)性地址對(duì)應(yīng)的物理地址。圖2.6是利用二級(jí)頁(yè)表進(jìn)行地址轉(zhuǎn)換的過(guò)程。圖2.6線(xiàn)性地址到物理地址的轉(zhuǎn)換(4KB頁(yè))在做線(xiàn)性地址到物理地址的轉(zhuǎn)換時(shí),必須知道所用頁(yè)目錄的位置。IA-32體系結(jié)構(gòu)專(zhuān)門(mén)提供了一個(gè)CR3寄存器,用于存放當(dāng)前使用的頁(yè)目錄的物理地址,因此CR3又叫頁(yè)目錄基地址寄存器(PDBR)。在啟動(dòng)分頁(yè)機(jī)制之前,必須定義好頁(yè)目錄并將其基地址裝入到CR3寄存器中。只要進(jìn)程在活動(dòng),它的頁(yè)目錄就應(yīng)該一直駐留在內(nèi)存。
當(dāng)然,頁(yè)目錄項(xiàng)也可以直接指向物理頁(yè),此時(shí)的頁(yè)大小是4MB。采用4MB頁(yè)可加快地址轉(zhuǎn)換的速度,因而通常將操作系統(tǒng)內(nèi)核所占用的頁(yè)設(shè)為4MB頁(yè)。當(dāng)頁(yè)目錄項(xiàng)的PS位為1時(shí),它所描述的是一個(gè)4MB頁(yè)而不再是一個(gè)頁(yè)表。
頁(yè)式管理機(jī)制是由操作系統(tǒng)內(nèi)核啟動(dòng)的,啟動(dòng)的方法是將CR0中的PG標(biāo)志置1。啟動(dòng)分頁(yè)機(jī)制之后,每個(gè)線(xiàn)性地址都需要經(jīng)過(guò)頁(yè)目錄和頁(yè)表的轉(zhuǎn)換,這顯然會(huì)大大降低內(nèi)存訪(fǎng)問(wèn)的速度。為解決這一問(wèn)題,IA-32體系結(jié)構(gòu)在其處理器芯片中增加了一個(gè)高速緩存TLB(TranslationLookasideBuffers),在其中存儲(chǔ)最近使用的頁(yè)目錄和頁(yè)表項(xiàng)。地址轉(zhuǎn)換時(shí),首先查T(mén)LB,如其中有緩存的頁(yè)表項(xiàng),可立刻進(jìn)行地址轉(zhuǎn)換;只有當(dāng)TLB中沒(méi)有對(duì)應(yīng)的頁(yè)表項(xiàng)時(shí),才會(huì)訪(fǎng)問(wèn)頁(yè)目錄和頁(yè)表。新訪(fǎng)問(wèn)的頁(yè)表項(xiàng)會(huì)被自動(dòng)加入TLB。
TLB的內(nèi)容必須經(jīng)常刷新以保持與頁(yè)目錄和頁(yè)表的一致。刷新工作由操作系統(tǒng)內(nèi)核負(fù)責(zé)。當(dāng)頁(yè)目錄或頁(yè)表項(xiàng)改變時(shí),內(nèi)核必須立刻使TLB中的相應(yīng)項(xiàng)失效。特別地,當(dāng)CR3改變時(shí),TLB中的所有內(nèi)容(Global頁(yè)除外)會(huì)自動(dòng)失效。INVLPG指令可以將TLB中的指定項(xiàng)設(shè)為無(wú)效。2.2.3內(nèi)存管理的變化與擴(kuò)展
段頁(yè)式內(nèi)存管理是Intel處理器提供的最基礎(chǔ)的內(nèi)存管理機(jī)制,在此基礎(chǔ)上,Intel還提供了許多變化與擴(kuò)展。
(1)基本平板式內(nèi)存管理。基本思路是屏蔽掉段式管理,完全采用頁(yè)式管理。做法是定義一個(gè)代碼段、一個(gè)數(shù)據(jù)段,兩個(gè)段的基地址都是0,大小都是4GB。如此以來(lái),邏輯地址就是線(xiàn)性地址,段式管理的作用被屏蔽。對(duì)段內(nèi)內(nèi)存(實(shí)際是所有內(nèi)存)的管理完全依靠分頁(yè)機(jī)制。
(2)保護(hù)平板式內(nèi)存管理?;舅悸肥瞧帘蔚舳问焦芾恚A粢恍┍Wo(hù)特征,主要采用頁(yè)式管理。一種典型的做法是定義四個(gè)段,即內(nèi)核代碼段、內(nèi)核數(shù)據(jù)段、用戶(hù)代碼段、用戶(hù)數(shù)據(jù)段,四個(gè)段的基地址都是0,大小都是4GB,不同的是內(nèi)核段的特權(quán)級(jí)是0,用戶(hù)段的特權(quán)級(jí)是3。將操作系統(tǒng)內(nèi)核的代碼和數(shù)據(jù)都放在內(nèi)核段中,將所有用戶(hù)的代碼和數(shù)據(jù)都放在用戶(hù)段中(不做區(qū)分)。進(jìn)程執(zhí)行用戶(hù)代碼時(shí)使用用戶(hù)段,執(zhí)行內(nèi)核代碼時(shí)使用內(nèi)核段。段的地址轉(zhuǎn)化作用被屏蔽,但基于特權(quán)級(jí)的保護(hù)特征被保留,如用戶(hù)代碼不能訪(fǎng)問(wèn)內(nèi)核數(shù)據(jù)等。段內(nèi)內(nèi)存用頁(yè)式管理。一般情況下,每個(gè)進(jìn)程都會(huì)用到四個(gè)段,因而需要為每個(gè)進(jìn)程定義四套頁(yè)目錄/頁(yè)表。但對(duì)同一個(gè)進(jìn)程來(lái)說(shuō),由于它對(duì)四個(gè)段的使用絕對(duì)不會(huì)重疊,因而可以將四個(gè)段疊加起來(lái),看成是進(jìn)程的平板地址空間,同時(shí)也可將四套頁(yè)目錄/頁(yè)表合并為一套。在合并后的頁(yè)目錄/頁(yè)表中,頁(yè)表項(xiàng)可能代表不同段中的頁(yè)。頁(yè)表項(xiàng)中的U/S標(biāo)志用于區(qū)分用戶(hù)頁(yè)和內(nèi)核頁(yè)。如果操作系統(tǒng)內(nèi)核能保證各進(jìn)程的頁(yè)目錄/頁(yè)表中沒(méi)有重疊的表項(xiàng),就可以保證進(jìn)程之間的相互隔離。
(3)多段式內(nèi)存管理?;舅悸肥峭耆捎枚问焦芾?,屏蔽掉頁(yè)式管理。
(4)基于物理地址擴(kuò)展的頁(yè)式內(nèi)存管理。從PentiumPro開(kāi)始,IA-32體系結(jié)構(gòu)引入了物理地址擴(kuò)展(PAE)機(jī)制以支持36位物理地址。在該管理模式中,處理器可訪(fǎng)問(wèn)的物理地址空間被擴(kuò)充到了64GB,但線(xiàn)性地址空間仍然為4GB,然而一個(gè)線(xiàn)性頁(yè)可以映射到任意一個(gè)物理頁(yè)。為做到這一點(diǎn),頁(yè)目錄和頁(yè)表項(xiàng)被擴(kuò)充到了64位,因而一個(gè)頁(yè)目錄或頁(yè)表中的項(xiàng)數(shù)變成了512,一個(gè)頁(yè)目錄僅能描述1GB的線(xiàn)性地址空間,4GB的線(xiàn)性地址空間需要4個(gè)頁(yè)目錄描述。新引入一個(gè)僅有4個(gè)表項(xiàng)的頁(yè)目錄指針表PDP(Directory-PointerTable),其中的每個(gè)表項(xiàng)指向一個(gè)頁(yè)目錄(一個(gè)PDP可以描述4GB的空間),CR3指向PDP。地址轉(zhuǎn)換機(jī)制被修改,以便將32位線(xiàn)性地址翻譯成36位物理地址。當(dāng)頁(yè)目錄項(xiàng)中的PS位被置1時(shí),它描述的頁(yè)變成了2MB頁(yè)。
(5)
64位平板內(nèi)存管理。在64位模式中,段通常被關(guān)閉,雖然不是完全關(guān)閉。處理器將CS、DS、ES、SS的基地址統(tǒng)統(tǒng)看成0,而且不再做段界限檢查,因而邏輯地址就是線(xiàn)性地址。但FS和GS的基地址可以不是0。如果FS、GS的基地址不是0,在將邏輯地址轉(zhuǎn)換成線(xiàn)性地址時(shí)要加上FS、GS的基地址。值得注意的是,F(xiàn)S、GS的基地址是64位地址(兼容模式只用它的低32位),記錄在MSR中。在64位模式中,對(duì)內(nèi)存的管理完全依靠分頁(yè)機(jī)制。Intel64體系結(jié)構(gòu)擴(kuò)展了PAE機(jī)制,使之能支持64位線(xiàn)性地址和52位物理地址。主要的擴(kuò)展包括:頁(yè)目錄指針表(PDP)被擴(kuò)充到了512項(xiàng);新引入一個(gè)第四級(jí)頁(yè)映射表PML4(PageMapLevel4Table),它的每個(gè)表項(xiàng)可指向一個(gè)PDP;所有四級(jí)頁(yè)表的表項(xiàng)都被擴(kuò)充到了64位(PAE必須使能);頁(yè)目錄項(xiàng)中的PS標(biāo)志用于控制4KB和2MB頁(yè);CR3指向PML4;在所有頁(yè)表項(xiàng)的第63位上新增了一個(gè)執(zhí)行禁止標(biāo)志EXB(Execute-DisableBit),如果該標(biāo)志被置1,它對(duì)應(yīng)的頁(yè)只能用做數(shù)據(jù)頁(yè),不能用做代碼頁(yè),即其上的代碼被禁止執(zhí)行。2.2.4內(nèi)存保護(hù)
一旦保護(hù)機(jī)制被啟動(dòng),處理器就會(huì)對(duì)每一次內(nèi)存訪(fǎng)問(wèn)進(jìn)行保護(hù)性檢查,以確保所有的訪(fǎng)問(wèn)都滿(mǎn)足保護(hù)策略。當(dāng)發(fā)現(xiàn)違反內(nèi)存保護(hù)約定的內(nèi)存訪(fǎng)問(wèn)時(shí),處理器會(huì)產(chǎn)生異常。由于保護(hù)檢查和地址轉(zhuǎn)換是并行進(jìn)行的,因而檢查本身并不會(huì)帶來(lái)額外的開(kāi)銷(xiāo)。
保護(hù)檢查包括段級(jí)檢查和頁(yè)級(jí)檢查兩種,檢查的依據(jù)是段描述符、頁(yè)目錄和頁(yè)表,檢查的順序是先段后頁(yè),檢查的基礎(chǔ)是特權(quán)級(jí)。特權(quán)級(jí)是Intel為實(shí)現(xiàn)保護(hù)而定義的特權(quán)編號(hào),從0到3,其中0是最高特權(quán)級(jí),3是最低特權(quán)級(jí)。系統(tǒng)中每個(gè)段(代碼段、數(shù)據(jù)段、堆棧段等)都有特權(quán)級(jí),因而系統(tǒng)中所有的程序與所有的數(shù)據(jù)也都有特權(quán)級(jí)。
段一級(jí)的檢查包括段界限檢查、段類(lèi)型檢查、特權(quán)級(jí)檢查、長(zhǎng)指針檢查等。段一級(jí)檢查的原則是:
(1)低特權(quán)級(jí)的代碼不能訪(fǎng)問(wèn)高特權(quán)級(jí)的數(shù)據(jù)。
(2)高特權(quán)級(jí)的代碼可以訪(fǎng)問(wèn)低特權(quán)級(jí)的數(shù)據(jù)。
(3)代碼只能使用與其特權(quán)級(jí)相同的堆棧,當(dāng)特權(quán)級(jí)切換時(shí),堆棧也要隨之切換。
(4)只能向具有相同特權(quán)級(jí)的非相容代碼段轉(zhuǎn)移控制(長(zhǎng)JMP和長(zhǎng)CALL)。
(5)可以向具有同等或較高特權(quán)級(jí)的相容代碼段轉(zhuǎn)移控制,但不能向具有較低特權(quán)級(jí)的相容代碼段轉(zhuǎn)移控制(長(zhǎng)JMP和長(zhǎng)CALL)。
(6)即使使用調(diào)用門(mén)、中斷門(mén)或陷阱門(mén),也不能從高特權(quán)級(jí)向低特權(quán)級(jí)轉(zhuǎn)移控制。
(7)不允許用長(zhǎng)RET向高特權(quán)級(jí)轉(zhuǎn)移控制。頁(yè)一級(jí)的檢查包括特權(quán)級(jí)檢查和讀寫(xiě)檢查,相關(guān)的標(biāo)志是頁(yè)目錄/頁(yè)表項(xiàng)中的U/S和R/W位。U/S為0的頁(yè)是超級(jí)頁(yè),U/S為1的頁(yè)是用戶(hù)頁(yè)。一般情況下,超級(jí)頁(yè)中的代碼可以訪(fǎng)問(wèn)所有的頁(yè)(不管R/W標(biāo)志),用戶(hù)頁(yè)中的代碼只能訪(fǎng)問(wèn)用戶(hù)頁(yè)。當(dāng)CR0.WP被置1時(shí),超級(jí)頁(yè)中的代碼也不能寫(xiě)只讀的用戶(hù)頁(yè)。
在Intel64體系結(jié)構(gòu)中,新引入了執(zhí)行禁止標(biāo)志NXB,用于防止緩沖區(qū)溢出之類(lèi)的攻擊。將IA32_EFER.NXE置1可使能頁(yè)級(jí)執(zhí)行檢查,此后NXB被置1的頁(yè)僅能用作數(shù)據(jù)頁(yè),試圖執(zhí)行數(shù)據(jù)頁(yè)中的指令會(huì)引起處理器異常。2.2.5進(jìn)程管理
當(dāng)處理器運(yùn)行在保護(hù)模式時(shí),它的所有工作都是在任務(wù)進(jìn)程中完成的,因而至少需定義一個(gè)任務(wù)。任務(wù)由執(zhí)行空間和任務(wù)狀態(tài)段組成。執(zhí)行空間由代碼段、堆棧段和數(shù)據(jù)段表示,它們的描述符可直接放在GDT或任務(wù)的LDT中。任務(wù)狀態(tài)段(TSS)用于記錄任務(wù)的狀態(tài),如通用寄存器及EFAGS、EIP、CR3、LDTR、TR寄存器的狀態(tài),0、1、2級(jí)堆棧的棧底指針等。一個(gè)TSS可唯一地描述一個(gè)任務(wù),如圖2.7所示。
圖2.732位任務(wù)狀態(tài)段(TSS)在IA-32e模式中,TSS被大大精簡(jiǎn),僅剩余了三個(gè)棧底指針和一個(gè)I/O許可位圖,但新增加了一個(gè)中斷棧表(7個(gè)棧指針)。
任務(wù)狀態(tài)段由專(zhuān)門(mén)的TSS描述符描述。當(dāng)前任務(wù)的TSS描述符被記錄在專(zhuān)門(mén)的任務(wù)寄存器(TR)中。通過(guò)TSS描述符或任務(wù)門(mén),利用CALL、JMP等指令可自動(dòng)完成任務(wù)切換,但代價(jià)很高?,F(xiàn)代操作系統(tǒng)內(nèi)核都選用代價(jià)更低的切換方法,如通過(guò)指令保存和恢復(fù)必要的寄存器從而完成任務(wù)切換。64位模式已不再支持自動(dòng)任務(wù)切換。由于CR3在TSS中,因而當(dāng)進(jìn)程切換時(shí),頁(yè)目錄也會(huì)隨著切換,也就是說(shuō),每個(gè)進(jìn)程都可以有自己獨(dú)立的線(xiàn)性地址空間。但為了系統(tǒng)能夠正常運(yùn)行,在任何時(shí)候處理器都應(yīng)該能夠訪(fǎng)問(wèn)到所有進(jìn)程的TSS,即應(yīng)該將TSS保存在所有進(jìn)程都可訪(fǎng)問(wèn)到的共享地址空間中。進(jìn)一步地,GDT、IDT、操作系統(tǒng)內(nèi)核代碼和系統(tǒng)管理信息等都應(yīng)該保存在共享地址空間中。事實(shí)上,在所有進(jìn)程的頁(yè)目錄中,確實(shí)存在著共用的頁(yè)表,這些共用的頁(yè)表描述的就是進(jìn)程間共享的線(xiàn)性地址空間。2.2.6中斷處理
Intel的中斷是外部中斷、異常和陷入的統(tǒng)稱(chēng)。外部中斷來(lái)自處理器之外的硬件,如外設(shè),是隨機(jī)的;異常來(lái)自于處理器內(nèi)部,表示在處理器執(zhí)行指令的過(guò)程中檢測(cè)到了某種錯(cuò)誤條件(如被0除、段越界等);陷入來(lái)自程序,由INTn、INTO等指令產(chǎn)生。外部中斷可以被屏蔽,但陷入和異常不能被屏蔽。屏蔽中斷的方法是清除EFLAGS寄存器中的IF標(biāo)志。中斷的產(chǎn)生表示系統(tǒng)中出現(xiàn)了某種必須引起處理器關(guān)注的事件,處理器需要立刻離開(kāi)當(dāng)前的工作轉(zhuǎn)去處理這些事件。處理中斷的程序稱(chēng)為中斷處理器程序,處理異常的程序稱(chēng)為異常處理程序,處理陷入的程序稱(chēng)為系統(tǒng)調(diào)用服務(wù)程序。處理程序可位于內(nèi)核空間的任意位置,且可有不同的特權(quán)級(jí),因而需要專(zhuān)門(mén)的數(shù)據(jù)結(jié)構(gòu)來(lái)描述它們。Intel處理器稱(chēng)處理程序的入口為門(mén)(Gate)。可用的門(mén)有三類(lèi),分別是中斷門(mén)、陷阱門(mén)和任務(wù)門(mén)(Linux只用到了中斷門(mén)和陷阱門(mén))。中斷門(mén)和陷阱門(mén)是進(jìn)入中斷和異常處理程序的門(mén)戶(hù),分別由中斷門(mén)和陷阱門(mén)描述符定義。中斷門(mén)描述符的格式如圖2.8所示。圖2.8中斷門(mén)描述符陷阱門(mén)描述符與中斷門(mén)描述符的格式基本一致,所不同的是陷阱門(mén)描述符中的類(lèi)型是“D111”。類(lèi)型中的D表示位數(shù),0為16位,1為32位。在兩種門(mén)描述符中,選擇符與偏移量合起來(lái)定義了一個(gè)處理器程序的入口地址,DPL定義了門(mén)的特權(quán)級(jí)。
中斷門(mén)與陷阱門(mén)的功能也基本一致,都定義了一個(gè)處理程序的入口地址,所不同的是處理IF標(biāo)志的方式。當(dāng)通過(guò)中斷門(mén)進(jìn)入處理程序時(shí),IF標(biāo)志被清掉(中斷被關(guān)閉);當(dāng)通過(guò)陷阱門(mén)進(jìn)入處理程序時(shí),IF標(biāo)志保持不變。為了處理中斷,Intel處理器給它的每個(gè)中斷和異常都賦予了一個(gè)中斷向量號(hào),并定義一個(gè)中斷描述符表(IDT)用于建立中斷向量號(hào)和門(mén)之間的對(duì)應(yīng)關(guān)系。
Intel處理器定義的中斷向量號(hào)共256個(gè),其中0~31被處理器保留,主要用于異常和不可屏蔽中斷(NMI),32~255可由操作系統(tǒng)內(nèi)核自由使用,如賦給外設(shè)等。
IDT是一個(gè)描述符數(shù)組,由一組門(mén)描述符組成,每一個(gè)中斷向量號(hào)對(duì)應(yīng)其中的一個(gè)門(mén)描述符。因?yàn)橹挥?56個(gè)中斷和異常,所以IDT只有256項(xiàng)。與GDT相同,IDT也不是一個(gè)段,沒(méi)有對(duì)應(yīng)的段描述符。IDT可以駐留在線(xiàn)性地址空間的任何位置。Intel處理器專(zhuān)門(mén)提供了一個(gè)IDTR寄存器來(lái)記錄IDT的基地址和界限信息。當(dāng)中斷或異常發(fā)生時(shí),處理器以中斷向量號(hào)為索引查IDT可得到與之對(duì)應(yīng)的門(mén)描述符,從而可得到處理程序的入口地址。圖2.9是IDTR和IDT之間關(guān)系。圖2.9IDTR與IDT之間的關(guān)系外部中斷被處理完后,處理器會(huì)接著執(zhí)行被中斷指令的下一條指令。陷入指令被處理完后,處理器會(huì)接著執(zhí)行陷入指令的下一條指令。異常處理程序被執(zhí)行完后,處理器的返回位置依賴(lài)于異常的類(lèi)型。Intel處理器目前定義了三類(lèi)共20個(gè)異常。故障(Fault)類(lèi)異常是可以更正的,當(dāng)故障類(lèi)異常被處理完后,處理器會(huì)重新執(zhí)行產(chǎn)生故障的指令。陷阱(Trap)類(lèi)異常是由特殊的陷入指令(INT3、INTO)引發(fā)的,當(dāng)陷阱類(lèi)異常被處理完后,處理器會(huì)接著執(zhí)行陷入指令的下一條指令。中止(Abort)類(lèi)異常是嚴(yán)重的錯(cuò)誤,處理器無(wú)法保證程序能夠繼續(xù)正常執(zhí)行。
Intel規(guī)定,通過(guò)中斷門(mén)或陷阱門(mén)只能向同級(jí)或更高特權(quán)級(jí)的代碼段轉(zhuǎn)移控制,因而通常將處理程序定義在0特權(quán)級(jí)的代碼段(內(nèi)核代碼段)中。
通過(guò)陷入指令也可以進(jìn)入中斷或異常處理程序,但要進(jìn)行特權(quán)級(jí)檢查,要求進(jìn)入前的特權(quán)級(jí)(CPL)必須小于或等于門(mén)描述符的特權(quán)級(jí)(DPL)。中斷或陷阱門(mén)的DPL通常被設(shè)為0,因而用戶(hù)程序無(wú)法通過(guò)INTn指令直接進(jìn)入中斷或異常處理程序。中斷或異常處理程序運(yùn)行在當(dāng)前進(jìn)程的上下文中,但可能使用不同的堆棧。如果中斷或異常發(fā)生時(shí)處理器在第0特權(quán)級(jí)上(在執(zhí)行內(nèi)核),則處理程序可直接使用當(dāng)前進(jìn)程的系統(tǒng)堆棧,不用切換堆棧。如果中斷或異常發(fā)生時(shí)處理器在第3特權(quán)級(jí)上(在執(zhí)行用戶(hù)程序),則需要切換堆棧,即從當(dāng)前進(jìn)程的用戶(hù)堆棧切換到它的系統(tǒng)堆棧。TR中記錄著當(dāng)前進(jìn)程的TSS,其中包含當(dāng)前進(jìn)程的系統(tǒng)堆棧的棧底。
中斷或異常發(fā)生時(shí),處理器會(huì)自動(dòng)在棧頂壓入一些參數(shù),其中EFLAGS是中斷或異常發(fā)生前的系統(tǒng)狀態(tài),SS:ESP是中斷或異常發(fā)生前的用戶(hù)堆棧棧頂,CS:EIP是中斷或異常的返回地址。只有發(fā)生堆棧切換時(shí),才會(huì)在棧頂壓入SS:ESP。
僅有一些特殊的異常會(huì)在棧頂壓入error-code(見(jiàn)表3.3),外部中斷和陷入不會(huì)自動(dòng)在棧頂壓入error-code。為了使堆棧保持平衡,對(duì)不自動(dòng)壓入error-code的中斷和異常,處理程序應(yīng)在棧頂壓入一個(gè)值,如Linux的外部中斷處理程序會(huì)壓入中斷向量號(hào)、無(wú)error-code的異常處理程序會(huì)壓入0或-1。圖2.10是中斷發(fā)生時(shí)堆棧的變化情況。圖2.10中斷或異常發(fā)生時(shí)的堆棧變化在64位模式中,中斷和異常的處理方式有所改變,如處理程序必須在64位代碼段中,因而中斷和陷阱門(mén)描述符被擴(kuò)充到了16字節(jié),其中的偏移量被擴(kuò)充到了64位;IDT中僅有新格式的門(mén)描述符;堆棧寬度變成了64位,而且當(dāng)中斷發(fā)生時(shí),會(huì)無(wú)條件地壓入棧指針(SS:RSP);當(dāng)需要切換堆棧時(shí),新的SS被強(qiáng)制設(shè)為NULL;新增加了的中斷堆棧表(IST)機(jī)制,允許為特定的中斷或異常指定專(zhuān)門(mén)的堆棧。2.2.7APIC
高級(jí)可編程中斷控制器(AdvancedProgrammableInterruptController,APIC)是老式8259中斷控制器(PIC)的升級(jí)產(chǎn)品,APIC本身的升級(jí)版分別叫做xAPIC和x2APIC。
APIC采用分布式體系結(jié)構(gòu),由LocalAPIC和I/OAPIC通過(guò)專(zhuān)用總線(xiàn)或系統(tǒng)總線(xiàn)互連構(gòu)成,如圖2.11所示。圖2.11APIC結(jié)構(gòu)
I/OAPIC接收來(lái)自設(shè)備的中斷,把它們傳遞給選定的一個(gè)或一組LocalAPIC。LocalAPIC可以接收外部的(來(lái)自I/OAPIC或8259A)、內(nèi)部的(來(lái)自L(fǎng)ocalAPIC的內(nèi)部時(shí)鐘等)或來(lái)自其它處理器的(IPI)中斷,并把它們傳遞給處理器核。LocalAPIC通常被集成在處理器核中。每個(gè)LocalAPIC有一個(gè)唯一的標(biāo)識(shí)符(ID),用于標(biāo)識(shí)LocalAPIC,也用于標(biāo)識(shí)與之關(guān)聯(lián)的處理器核。
LocalAPIC的內(nèi)部中斷通常有5~6個(gè),包括LocalAPIC定時(shí)器、溫度傳感器、性能計(jì)數(shù)器、內(nèi)部錯(cuò)誤、LINT0和LINT1等,其中LINT0和LINT1是兩個(gè)管腳,用于連接其它中斷源,如8259PIC。LocalAPIC提供了一個(gè)局部中斷向量表,用于設(shè)定各個(gè)內(nèi)部中斷的向量號(hào)、遞交方式等??梢詫INT0或LINT1的中斷遞交方式設(shè)為ExtINT,此時(shí)處理器認(rèn)為該管腳上連接的是PIC,會(huì)按通常的應(yīng)答方式獲取中斷向量號(hào)。
對(duì)操作系統(tǒng)內(nèi)核來(lái)說(shuō),LocalAPIC由一組寄存器構(gòu)成,包括LocalAPICID、LocalAPICVersion、TaskPriorityRegister(TPR)、ProcessorPriorityRegister(PPR)、In-ServiceRegister(ISR)、InterruptRequestRegister(IRR)、InterruptCommandRegister(ICR)、LocalVectorTable(LVT)、EndofInterruptRegister等。這些寄存器被映射到處理器物理地址空間中的一個(gè)4KB大小的區(qū)域內(nèi),缺省的起始地址為0xFEE00000。操作系統(tǒng)內(nèi)核可以將APIC的寄存器重新映射到物理地址空間的其它區(qū)域。LocalAPIC的狀態(tài)和寄存器基地址記錄在MSR寄存器IA32_APIC_BASE中。
多核處理器系統(tǒng)中有多個(gè)LocalAPIC,它們的寄存器都被映射到相同的位置。每一個(gè)邏輯處理器都可以訪(fǎng)問(wèn)該映射頁(yè),但訪(fǎng)問(wèn)的結(jié)果各不相同。當(dāng)邏輯處理器讀寫(xiě)LocalAPIC映射頁(yè)時(shí),它實(shí)際上訪(fǎng)問(wèn)的是自己的LocalAPIC。
I/OAPIC也由一組寄存器組成,包括I/OAPICID、I/OAPICVER、I/OAPICARB、I/OREDTBL等。其中I/OREDTBL是一個(gè)中斷重定向表,用于確定各外部中斷的遞交目的地、向量號(hào)、遞交模式等。與LocalAPIC的寄存器不同,I/OAPIC中的寄存器只能用間接方法訪(fǎng)問(wèn),方法是先將要訪(fǎng)問(wèn)寄存器的偏移量寫(xiě)入選擇寄存器IOREGSEL,而后再讀或?qū)憯?shù)據(jù)寄存器IOWIN。
每一個(gè)I/OAPIC都有一個(gè)物理基地址ioapicaddr,這一地址實(shí)際就是該I/OAPIC中寄存器IOREGSEL的物理地址,寄存器IOWIN的物理地址是ioapicaddr+0x10。缺省情況下,ioapicaddr是0xFEC00000。查ACPI或MP表可獲得各I/OAPIC的基地址。2.2.8處理器初始化
從P6系列開(kāi)始,在IA-32體系結(jié)構(gòu)中增加了一個(gè)多處理器初始化協(xié)議(MP),用于規(guī)定多處理器系統(tǒng)的初始化過(guò)程。MP協(xié)議將處理器分為自舉處理器BSP(BootstrapProcessor)和應(yīng)用處理器AP(ApplicationProcessor)。在系統(tǒng)加電或Reset之后,多處理器系統(tǒng)中的系統(tǒng)硬件會(huì)動(dòng)態(tài)地選舉出一個(gè)處理器為BSP,其余處理器為AP。
MP協(xié)議僅在加電或Reset之后執(zhí)行一次,此后的INIT不會(huì)再執(zhí)行MP協(xié)議。MP協(xié)議規(guī)定的處理器初始化過(guò)程如下:
(1)根據(jù)系統(tǒng)拓?fù)浣Y(jié)構(gòu),給系統(tǒng)總線(xiàn)上的每一個(gè)邏輯處理器一個(gè)唯一的8位APICID。該ID號(hào)被寫(xiě)入處理器的局部APICID寄存器中。
(2)根據(jù)APICID為每個(gè)邏輯處理器賦予一個(gè)唯一的仲裁優(yōu)先級(jí)。
(3)各邏輯處理器同時(shí)執(zhí)行自己的內(nèi)建自檢代碼BIST。
(4)BIST執(zhí)行完畢之后,系統(tǒng)總線(xiàn)上的各邏輯處理器利用硬件定義的選擇機(jī)制選舉出BSP和AP。而后,BSP開(kāi)始執(zhí)行BIOS代碼,各AP進(jìn)入等待狀態(tài)。
(5)
BSP創(chuàng)建一個(gè)ACPI表和一個(gè)MP表,并將它自己的APICID填入其中。
(6)在自舉程序執(zhí)行完后,BSP將處理器計(jì)數(shù)器設(shè)置為1,而后向所有的AP廣播SISP消息。SISP消息中包含一個(gè)向量,指出各AP開(kāi)始執(zhí)行的初始化代碼的位置。
(7)AP申請(qǐng)初始化信號(hào)量,在獲得信號(hào)量后開(kāi)始執(zhí)行初始化代碼,將自己的APICID填入ACPI表和MP表,將處理器計(jì)數(shù)器加1。在初始化代碼執(zhí)行完畢之后,AP執(zhí)行CLI和HLT指令進(jìn)入停止?fàn)顟B(tài)。
(8)在所有AP都執(zhí)行完初始化代碼之后,BSP通過(guò)處理器計(jì)數(shù)器獲得連接在系統(tǒng)總線(xiàn)上的邏輯處理器數(shù),而后執(zhí)行進(jìn)一步的自舉和啟動(dòng)代碼,如內(nèi)核初始化代碼等。
(9)在BSP執(zhí)行內(nèi)核初始化代碼期間,各AP一直處于停止?fàn)顟B(tài),等待被BSP的處理器間中斷信號(hào)IPI喚醒。2.2.9寄存器與特權(quán)指令I(lǐng)A-32體系結(jié)構(gòu)提供了8個(gè)32位的通用寄存器,分別稱(chēng)為EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP。Intel64體系結(jié)構(gòu)將這8個(gè)通用寄存器擴(kuò)充到了64位,分別稱(chēng)為RAX、RBX、RCX、RDX、RSI、RDI、RSP、RBP,并另外引入了8個(gè)通用寄存器,分別稱(chēng)為R8、R9、R10、R11、R12、R13、R14、R15。
IA-32體系結(jié)構(gòu)提供了6個(gè)段寄存器,即CS、DS、SS、ES、FS、GS。在64模式中,DS、ES、SS已不再使用,F(xiàn)S、GS用于段重載(影響段的基地址),CS用于控制64位模式與兼容模式的切換。在IA-32體系結(jié)構(gòu)中,指令寄存器是EIP,長(zhǎng)度為32位,記錄下一條要執(zhí)行的指令地址。在Intel64體系結(jié)構(gòu)中,指令寄存器被擴(kuò)充到了64位,稱(chēng)為RIP。
IA-32體系結(jié)構(gòu)提供了一個(gè)32位的標(biāo)志寄存器EFLAGS,用于存放處理器的狀態(tài)信息(如ZF、CF、OF等)和一些系統(tǒng)控制信息(如IF、IOPL、VM等)。在64位模式中,標(biāo)志寄存器被擴(kuò)充到了64位,稱(chēng)為RFLAGS,但內(nèi)容并未擴(kuò)展。
IA-32體系結(jié)構(gòu)提供了4個(gè)內(nèi)存管理寄存器,分別稱(chēng)為GDTR、LDTR、IDTR、TR。Intel64體系結(jié)構(gòu)將它們的基地址部分?jǐn)U充到了64位。
IA-32體系結(jié)構(gòu)提供了5個(gè)32位的控制寄存器,分別稱(chēng)為CR0、CR1、CR2、CR3、CR4。其中CR0中包含系統(tǒng)控制標(biāo)志,用于控制處理器的操作模式和狀態(tài),如是否啟用分頁(yè)機(jī)制等;CR1保留未用;CR2用于暫存引起頁(yè)故障異常(fault)的線(xiàn)性地址;CR3中暫存當(dāng)前使用的頁(yè)目錄的物理基地址;CR4中包含一組體系結(jié)構(gòu)擴(kuò)展標(biāo)志,如PAE、PSE等。Intel64體系結(jié)構(gòu)將控制寄存器擴(kuò)充到了64位,新增加了兩個(gè)標(biāo)志位,并引入了一個(gè)新的控制寄存器CR8,用于記錄任務(wù)的優(yōu)先級(jí)。
Intel處理器中還提供了一組專(zhuān)門(mén)給操作系統(tǒng)內(nèi)核使用的、與處理器型號(hào)相關(guān)的專(zhuān)用寄存器(MSR),用來(lái)控制處理器的debug擴(kuò)展、性能監(jiān)視、機(jī)器檢查結(jié)構(gòu)、內(nèi)存類(lèi)型范圍等。在不同的處理器中,MSR寄存器的個(gè)數(shù)和功能可能會(huì)有所變化。
Intel處理器還提供了8個(gè)調(diào)試寄存器(DR0-DR7),用于幫助Debug程序設(shè)置斷點(diǎn)。除了上述寄存器之外,Intel處理器還專(zhuān)門(mén)提供了一組系統(tǒng)指令,用來(lái)處理系統(tǒng)級(jí)的工作。如裝入系統(tǒng)寄存器、管理Cache、管理中斷、設(shè)置Debug寄存器等。有些系統(tǒng)指令只能由操作系統(tǒng)內(nèi)核執(zhí)行(要求特權(quán)級(jí)為0),另一些系統(tǒng)指令可在任意特權(quán)級(jí)下執(zhí)行。表2.1中是常用的幾條系統(tǒng)指令。
表2.1常用的系統(tǒng)指令
GNU的C語(yǔ)言是對(duì)標(biāo)準(zhǔn)C語(yǔ)言的擴(kuò)展,其編譯器稱(chēng)為GCC。GCC是RichardStallman于1984年發(fā)起的一個(gè)項(xiàng)目,最初的目的是開(kāi)發(fā)一個(gè)免費(fèi)的
C
編譯器,因而早期的意思是GNUCCompiler。由于GCC具有靈活的架構(gòu)并采取了開(kāi)源策略,因而發(fā)布之后迅速被接受、移植、擴(kuò)充,目前已可支持C、C++、Objective-C、Objective-C++、Java、Fortran、Ada等多種語(yǔ)言,支持30多種處理器家族,可在超過(guò)60種平臺(tái)上運(yùn)行?,F(xiàn)在GCC的意思變成了GNUCompilerCollection。2.3GNUC語(yǔ)言
GCC中的C編譯器是Linux的基礎(chǔ),Linux內(nèi)核源代碼只能用GCC編譯。GCC支持C語(yǔ)言標(biāo)準(zhǔn)C89、C90、C94、C95、C99等,并有自己的擴(kuò)展,如:
(1)允許將一個(gè)復(fù)合語(yǔ)句括在一對(duì)圓括號(hào)內(nèi)作為一個(gè)表達(dá)式使用,如:
#definemaxint(a,b)({int_a=(a),_b=(b);_a>_b?_a:_b;})
(2)允許在一個(gè)函數(shù)內(nèi)部定義嵌入式函數(shù),如:
foo(doublea,doubleb){
double square(doublez){returnz*z;}
return square(a)+square(b);
}
(3)可以直接將typeof作數(shù)據(jù)類(lèi)型使用,如:
typeof(*x)y[4]; //指針數(shù)組,指針?biāo)割?lèi)型是參數(shù)x的類(lèi)型
typedef typeof(expr)T; //T是expr的類(lèi)型
(4)可以忽略條件表達(dá)式的中間項(xiàng)。如x?:y等價(jià)于x?x:y。
(5)允許用longlongint聲明64位長(zhǎng)整數(shù),用unsignedlonglongint聲明64位無(wú)符號(hào)長(zhǎng)整數(shù)。后綴LL表示64位整常量,ULL表示64位無(wú)符號(hào)整常量。
(6)允許聲明長(zhǎng)度為0的數(shù)組。0長(zhǎng)度數(shù)組通常作為結(jié)構(gòu)的最后一個(gè)成員,用于表示一組變長(zhǎng)的對(duì)象。0長(zhǎng)度數(shù)組不占用結(jié)構(gòu)的空間,如:
structline{
int length;
char contents[0];
};
(7)允許在宏定義中使用可變數(shù)目的參數(shù),如:
#definedebug(format,args...)fprintf(stderr,format,args)
(8)允許在數(shù)組、結(jié)構(gòu)、聯(lián)合等類(lèi)型變量的聲明中使用指示初始化,如:
structpointp={.y=yvalue,.x=xvalue};
僅給x和y成員初值,未明確聲明成員的初值是0。
(9)在定義函數(shù)時(shí),允許用關(guān)鍵字_attribute_聲明屬性??梢月暶鞯膶傩园▽?duì)齊要求(aligned)、即將過(guò)時(shí)(deprecated)、快速調(diào)用(fastcall)、無(wú)返回(noreturn)、兩次返回(returns_twice)、函數(shù)所在節(jié)(section)、用寄存器傳遞參數(shù)的個(gè)數(shù)(regparm)等。如:
voidfatal() _attribute_((noreturn));
#define_init _attribute_((_section_(".init.text")))
#defineasmlinkage _attribute_((regparm(0)))第一個(gè)聲明表示函數(shù)fatal不返回,宏_ini表示將函數(shù)代碼放在.init.text節(jié)中,宏asmlinkage表示函數(shù)不用寄存器傳遞參數(shù),所有的參數(shù)都在堆棧中傳遞。
C語(yǔ)言函數(shù)之間通常用寄存器傳遞參數(shù),regparm(n)表示用寄存器傳遞n個(gè)參數(shù),前三個(gè)分別在EAX、EBX、ECX寄存器中。
(10)在定義結(jié)構(gòu)或聯(lián)合類(lèi)型時(shí),可以通過(guò)關(guān)鍵字_attribute_聲明相關(guān)的屬性。在聲明變量或結(jié)構(gòu)成員時(shí),可以通過(guò)關(guān)鍵字_attribute_聲明相關(guān)的屬性,如對(duì)齊要求、存放變量的節(jié)等。
(11)在內(nèi)嵌式匯編程序中,允許用C語(yǔ)言表達(dá)式做指令的操作數(shù),不用關(guān)心數(shù)據(jù)的存儲(chǔ)位置(見(jiàn)2.4.3節(jié))。
(12)提供了數(shù)百個(gè)內(nèi)建函數(shù)(多以_builtin_開(kāi)頭),用于實(shí)現(xiàn)內(nèi)存原子存取、對(duì)象大小檢查、程序優(yōu)化等,如:
#definelikely(x) _builtin_expect(!!(x),1) //x的預(yù)期值是真
#defineunlikely(x) _builtin_expect(!!(x),0) //x的預(yù)期值是假
語(yǔ)句if(unlikely(exp){…} 表示exp很少為真,編譯器可據(jù)此進(jìn)行優(yōu)化。
Linux內(nèi)核中的絕大部分代碼是用GNU的C語(yǔ)言寫(xiě)成的,但也包含一些匯編程序。Linux中的匯編程序可以以.S文件的形式單獨(dú)出現(xiàn),也可以嵌入在C代碼中。Linux中的匯編程序采用GNU的匯編格式,語(yǔ)法上符合AT&T規(guī)范。2.4GNU匯編語(yǔ)言2.4.1GNU匯編格式
GNU匯編程序由匯編指令、匯編指示、符號(hào)、常量、表達(dá)式、注釋等組成。為了便于管理和鏈接,通常將目標(biāo)程序中的代碼、數(shù)據(jù)等組織成不同的節(jié)(section)。節(jié)是具有相同屬性(如只讀)的一段連續(xù)的地址區(qū)間(在節(jié)中使用相對(duì)地址)。匯編器根據(jù)源程序中的匯編指示將源程序轉(zhuǎn)變成由若干個(gè)節(jié)組成的目標(biāo)文件,鏈接器負(fù)責(zé)將所有目標(biāo)文件中相同節(jié)的內(nèi)容拼接起來(lái)形成可執(zhí)行文件。一般情況下,匯編器生成的目標(biāo)文件中至少包含三個(gè)節(jié),分別是.text(只讀的程序代碼節(jié))、.data(可讀寫(xiě)的帶初值的變量節(jié))和.bss(可讀寫(xiě)的未初始化的變量節(jié))。用戶(hù)可以通過(guò)匯編指示.section聲明其它的節(jié),以便細(xì)化管理。如在Linux源代碼中經(jīng)常會(huì)看到下列格式的程序片段:
1: asminstruction //該指令在.text節(jié)中,它的執(zhí)行可能會(huì)出錯(cuò)
.section_ex_table,"a" //切換到_ex_table節(jié),在其中增加一對(duì)數(shù)據(jù)
.align4 //4對(duì)齊
.long1b,syscall_fault //異常處理表項(xiàng),意思是當(dāng)1處的指令出錯(cuò)時(shí),轉(zhuǎn)syscall_fault
.previous //切換回.text節(jié)
另外,在節(jié)中還可以再定義子節(jié),以便更好地組織分散在不同源程序中的、屬于同一個(gè)節(jié)的代碼和數(shù)據(jù)。子節(jié)的標(biāo)識(shí)是節(jié)名后加一個(gè)數(shù)字編號(hào),如.text.2,編號(hào)可以從0到8191。在目標(biāo)文件中,一個(gè)節(jié)的內(nèi)容就是它的各子節(jié)內(nèi)容的總和(按編號(hào)排序),但已沒(méi)有子節(jié)的概念。鏈接器只能看到節(jié),已看不到子節(jié)。如未為節(jié)定義子節(jié),匯編器會(huì)假定其中只有一個(gè)編號(hào)為0的子節(jié)??梢杂脜R編指示.subsection切換子節(jié),也可以用子節(jié)的標(biāo)識(shí)符切換子節(jié)。在GNU匯編程序中,常量是一個(gè)數(shù)字,其值是已知的,可直接使用。常量包括字符、字符串、整數(shù)(二進(jìn)制、八進(jìn)制、十進(jìn)制、十六進(jìn)制等)、大整數(shù)(超過(guò)32位)和浮點(diǎn)數(shù)。符號(hào)是由字母、數(shù)字、‘_’、‘.’、‘$’等組成的字符串,必須以字母開(kāi)頭,大小寫(xiě)有區(qū)別。符號(hào)用來(lái)給匯編程序中的對(duì)象命名,如標(biāo)號(hào)、常量等。符號(hào)有兩個(gè)屬性,分別是value和type??梢杂谩?’或“.set”改變符號(hào)的值?!?’是一個(gè)特殊的符號(hào),表示當(dāng)前地址。表達(dá)式是由操作符和括號(hào)連接起來(lái)的一組符號(hào)和常量,其結(jié)果是一個(gè)地址或一個(gè)數(shù)值。GNU匯編的表達(dá)式與C語(yǔ)言表達(dá)式基本一致。
GNU的匯編指示(AssemblerDirectives)又稱(chēng)偽操作(PseudoOps),是匯編程序提供給匯編器的指示或命令,用于聲明目標(biāo)文件的生成方法。所有的匯編指示都以‘.’開(kāi)頭。GNU匯編提供了100多個(gè)指示,在Linux源代碼中用到的有以下幾種:
(1)
.align
abs-expr1,
abs-expr2,
abs-expr3:用第2個(gè)表達(dá)式的值填充目標(biāo)文件中的當(dāng)前節(jié),使下一個(gè)可用位置是第1表達(dá)式的倍數(shù)允許跳過(guò)的最大字節(jié)數(shù)由第3個(gè)表達(dá)式?jīng)Q定。第2、3表達(dá)式都是可選的。
(2)
.balign[wl]
abs-expr1,
abs-expr2,
abs-expr3:.balign與.align的意思相同。變體.balignw表示填充值是2字節(jié)的字,.balignl表示填充值是4字節(jié)的長(zhǎng)字。
(3)
.p2align[wl]
abs-expr1,
abs-expr2,
abs-expr3:.p2align[wl]與.align[wl]相似,不同之處在于下一個(gè)可用位置是2
abs-expr1的倍數(shù)。
(4)
.org
new-lc,fill:從new-lc標(biāo)識(shí)的新位置開(kāi)始存放下面的代碼或數(shù)據(jù),空出來(lái)的空間用fill填充。新位置是在同一節(jié)中的偏移量。
(5)
.ascii"string"...:定義一到多個(gè)字符串。字符串后不自動(dòng)加0結(jié)尾。
(6)
.asciz"string"...:定義一到多個(gè)字符串。字符串后自動(dòng)以0結(jié)尾。
(7)
.string
"str":將字符串拷貝到目標(biāo)文件中,串以0結(jié)尾。
(8)
.byte
expressions:定義一到多個(gè)字節(jié)類(lèi)型(1字節(jié))的表達(dá)式。
(9)
.word
expressions:定義一到多個(gè)字類(lèi)型(2字節(jié))的表達(dá)式。
(10)
.long
expressions:定義一到多個(gè)長(zhǎng)整數(shù)(4字節(jié))類(lèi)型的表達(dá)式。
(11)
.quad
bignums:定義一到多個(gè)8字節(jié)的長(zhǎng)整數(shù)。
(12)
.fill
repeat,
size,
value:將value值拷貝repeat次,其中每個(gè)value占用size字節(jié)。如“.fill1024,4,0”會(huì)產(chǎn)生一個(gè)全0的頁(yè)。
(13)
.space
size,
fill和.skip
size,
fill:在目標(biāo)文件的當(dāng)前位置處留出size字節(jié)的空間,并在其中填入值fill,如未指定fill,則填入0。
(14)
.rept
count和.endr:將.rept和.endr之間的行重復(fù)count次。
(15)
.set
symbol,
expression:將符號(hào)symbol的值設(shè)為expression。
(16)
.typename,
@type:
將符號(hào)name的type屬性設(shè)為type。
其中type可以是function
(符號(hào)name是一個(gè)函數(shù)名)或object(符號(hào)name是一個(gè)數(shù)據(jù)對(duì)象)。
(17)
.sizename,expression:將符號(hào)name所占空間設(shè)為expression。
(18)
.global
symbol或.globl
symbol:使符號(hào)symbol成為全局的,即使該符號(hào)對(duì)鏈接程序是可見(jiàn)的。
(19)
.sectionname[,"flags"[,@type[,flag_specific_arguments]]]:切換當(dāng)前節(jié),即將下面的代碼或數(shù)據(jù)匯編到name節(jié)中。其中flags可以是a(節(jié)是可分配的)、w(節(jié)是可寫(xiě)的)、x(節(jié)是可執(zhí)行的);type可以是@progbits(節(jié)中包含數(shù)據(jù))、@nobits(節(jié)中不含數(shù)據(jù),只是占位空間)、@note(節(jié)中包含注釋信息,不是程序)。
(20)
.subsection
num:切換當(dāng)前子節(jié),即將下面的代碼或數(shù)據(jù)放在由num指定的子節(jié)中,節(jié)保持不變。
(21)
.text
subsection:切換當(dāng)前子節(jié),即將下面的程序匯編到.text節(jié)的編號(hào)為subsection的子節(jié)中。如未提供subsection,其缺省值為0。
(22)
.datasubsection:切換當(dāng)前子節(jié),即將下面的數(shù)據(jù)匯編到.data節(jié)的編號(hào)為subsection的子節(jié)中。如未提供subsection,其缺省值為0。
(23)
.previous:將當(dāng)前節(jié)換回到前一個(gè)節(jié)與子節(jié),即將下面的指令或數(shù)據(jù)匯編到當(dāng)前節(jié)之前使用的節(jié)與子節(jié)中,如:
.sectionA
.subsection1
.word0x1234
.subsection2
.word0x5678 //0x5678放在subsection2中
.previous
.word0x9abc //0x1234與0x9abc放在subsection1中(24)
.code16:將下面的程序匯編成16位代碼(實(shí)模式或保護(hù)模式)。
(25)
.code32:將下面的程序匯編成32位代碼。2.4.2AT&T指令語(yǔ)法
與Intel指令相比,AT&T格式的指令有如下特點(diǎn):
(1)指令操作數(shù)的順序是先源后目的,與Intel指令的先目的后源的順序相反。
(2)寄存器操作數(shù)前加前綴%,立即數(shù)前加前綴$。
(3)操作碼帶后綴以指明操作數(shù)的長(zhǎng)度。后綴有b(8位)、w(16位)、l(32位)、q(64位)。在新版本的GUN匯編中,可以不帶后綴。如:
moww%bx,%ax //將bx寄存器的內(nèi)容拷貝到ax寄存器中
movw$1,%ax //將ax寄存器的值設(shè)為常數(shù)1
movlX,%eax //將變量X的值傳遞到eax寄存器中
(4)符號(hào)常數(shù)直接引用,如:
value:.long0x12345678 //定義一個(gè)雙字類(lèi)型的符號(hào)常量value
movlvalue,%ebx //ebx的值是0x12345678
引用符號(hào)常量的地址時(shí),要在符號(hào)常量前加$,如:
movl$value,%ebx //將符號(hào)value的地址裝入ebx
(5)大部分指令的操作碼都與Intel指令相同,但也有幾個(gè)例外,如:
lcall$S,$O //長(zhǎng)調(diào)用,Intel的表示是callfarS:O
ljmp$S,$O //長(zhǎng)跳轉(zhuǎn),Intel的表示是jmpfarS:O
lret$V //長(zhǎng)返回,Intel的表示是retfarV
(6)內(nèi)存間接尋址的寫(xiě)法是disp(base,index,scale),其意思是地址[base+disp+index*scale],如:
movl4(%ebp),%eax //從地址[ebp+4]中取1個(gè)長(zhǎng)字給eax
movlary(,%eax,4),%eax //從地址[4*eax+ary]中取1個(gè)長(zhǎng)字給eax
movwary(%ebx,%eax,4),%cx //從地址[ebx+4*eax+ary]中取1個(gè)字給cx
(7)
call和jmp的操作數(shù)前可以加“*”,以表示絕對(duì)地址,未加“*”表示相對(duì)地址(相對(duì)于EIP)。如call*%edi。
(8)允許使用局部標(biāo)號(hào)(數(shù)字標(biāo)號(hào)),而且允許重復(fù)定義局部標(biāo)號(hào)。在以局部標(biāo)號(hào)為目的的轉(zhuǎn)移指令上,標(biāo)號(hào)要帶上后綴,b表示向后(已執(zhí)行過(guò)的部分),f表示向前(未執(zhí)行過(guò)的部分)。如:
1: jmp1f //跳到第3行
2: jmp1b //跳到第1行
1: jmp2f //跳到第4行
2: jmp1b //跳到第3行2.4.3GNU內(nèi)嵌匯編
GCC允許在C語(yǔ)言代碼中嵌入?yún)R編代碼,以實(shí)現(xiàn)C語(yǔ)言語(yǔ)法無(wú)法實(shí)現(xiàn)或不便實(shí)現(xiàn)的基礎(chǔ)操作,如讀寫(xiě)系統(tǒng)寄存器等。內(nèi)嵌匯編的格式也是AT&T的,如下:
_asm__volatile_(
asmstatements
:outputs
:inputs
:registers-modified
);各部分的意義如下:
(1)
_asm_是一個(gè)宏,用于聲明一個(gè)內(nèi)嵌的匯編表達(dá)式,是必不可少的關(guān)鍵字。
(2)
_volatile_是一個(gè)宏,用于聲明“不要優(yōu)化該段內(nèi)嵌匯編,讓它們保持原樣”。_volatile_是可選的。
(3)
asmstatements部分是一組AT&T格式的匯編語(yǔ)句,可以為空。一般情況下,在一行上應(yīng)只寫(xiě)一個(gè)匯編語(yǔ)句。如果需要在一行上寫(xiě)多個(gè)語(yǔ)句,它們之間要用分號(hào)或“\n”(換行)隔開(kāi)。所有的語(yǔ)句都要括在雙引號(hào)內(nèi),可以用一對(duì)雙引號(hào),也可以用多對(duì)雙引號(hào)。寄存器前面要加兩個(gè)%做前綴。支持局部標(biāo)號(hào),且可以使用數(shù)字標(biāo)號(hào)。
(4)
inputs部分指明內(nèi)嵌匯編程序的輸入?yún)?shù)。每個(gè)輸入?yún)?shù)都括在一對(duì)圓括號(hào)內(nèi),各參數(shù)間用逗號(hào)分開(kāi)。每個(gè)輸入?yún)?shù)前都要加一到多個(gè)用雙引號(hào)括起來(lái)的約束標(biāo)志,用于向編譯器聲明該參數(shù)的輸入位置(寄存器)及其相關(guān)信息。
(5)
outputs部分指明內(nèi)嵌匯編程序的輸出參數(shù)。每個(gè)輸出變量都括在一對(duì)圓括號(hào)內(nèi),各個(gè)輸出參數(shù)間用逗號(hào)隔開(kāi)。每個(gè)輸出參數(shù)前都要加一到多個(gè)用雙引號(hào)括起來(lái)的約束標(biāo)志,以告訴編譯器從何處輸出該參數(shù)及其相關(guān)信息。輸出約束標(biāo)志與輸入約束標(biāo)志相同,但前面還要多加一個(gè)“=”。輸出參數(shù)應(yīng)該是左值,而且必須是可寫(xiě)的。如果一個(gè)參數(shù)既做輸出又做輸入,可以在其前面加入“+”約束,也可以將它分成兩個(gè),一個(gè)寫(xiě)在outputs部分為只寫(xiě)參數(shù),一個(gè)寫(xiě)在inputs部分為只讀參數(shù)。
(6)輸入和輸出參數(shù)從0開(kāi)始統(tǒng)一編號(hào)。一個(gè)編號(hào)可以唯一標(biāo)識(shí)一個(gè)參數(shù)。在asmstatements部分可以通過(guò)標(biāo)識(shí)號(hào)(加前綴%)來(lái)引用參數(shù)。在inputs部分,可以用標(biāo)識(shí)號(hào)做輸入約束標(biāo)志(括在雙引號(hào)內(nèi)),告訴編譯器將該輸入?yún)?shù)與標(biāo)識(shí)號(hào)所標(biāo)識(shí)的輸出參數(shù)放在同一個(gè)寄存器中。
(7)
registers-modified告訴編譯器內(nèi)嵌匯編程序?qū)⒁薷牡募拇嫫鳌C總€(gè)寄存器都用雙引號(hào)括起來(lái),各寄存器間用逗號(hào)隔開(kāi)。如果內(nèi)嵌匯編程序中引用了某個(gè)特定的硬件寄存器,就應(yīng)該在此處列出該寄存器,以告訴編譯器這些寄存器的值被改變了。如果匯編程序中用某種不可預(yù)測(cè)的方式修改了內(nèi)存,應(yīng)該在此處加上“memory”。
內(nèi)嵌匯編中常用的約束標(biāo)志有下列幾種:
“g”:讓編譯器決定將參數(shù)裝入哪個(gè)寄存器。
“a”:將參數(shù)裝入到ax/eax,或從ax/eax輸出。
“b”:將參數(shù)裝入到bx/ebx,或從bx/ebx輸出。
“c”:將參數(shù)裝入到cx/ecx,或從cx/ecx輸出?!癲”:將參數(shù)裝入到dx/edx,或從dx/edx輸出。
“D”:將參數(shù)裝入到di/edi,或從di/edi輸出。
“S”:將參數(shù)裝入到si/esi,或從si/esi輸出。
“q”:可以將參數(shù)裝入到ax/eax、bx/ebx、cx/ecx或dx/edx寄存器中。
“r”:可以將參數(shù)裝入到任一通用寄存器中。
“i”:整型立即數(shù)。
“m”:內(nèi)存參數(shù)。
“p”:有效內(nèi)存地址。
“=”:輸出,參數(shù)是只寫(xiě)的左值。
“+”:既是輸入?yún)?shù)又是輸出參數(shù)?!?”:一般情況下,GCC將把輸出參數(shù)和一個(gè)不相干的輸入?yún)?shù)分配在同一個(gè)寄存器中,因?yàn)樗僭O(shè)在輸出產(chǎn)生之前,所有的輸入都已被消耗掉了。但如果內(nèi)嵌的匯編程序有多條指令,這種假設(shè)就不再正確。在輸出參數(shù)之前加入“&”,可以保證輸出參數(shù)不會(huì)覆蓋掉輸入?yún)?shù)。此時(shí),GCC將為該輸出參數(shù)分配一個(gè)輸入?yún)?shù)還沒(méi)有使用到的寄存器,除非特殊聲明(如用數(shù)字0~9)。
“0~9”:稱(chēng)為匹配約束標(biāo)志,用于約束一個(gè)既做輸入又做輸出的參數(shù),表示輸入?yún)?shù)和輸出參數(shù)占據(jù)同一個(gè)寄存器。數(shù)字約束標(biāo)志只能出現(xiàn)在輸入?yún)?shù)中,是與其共用同一位置的輸出參數(shù)的編號(hào)。
inputs、outputs和registers-modified部分都可有可無(wú)。如有,順序不能變;如無(wú),應(yīng)保留“:”,除非不引起二義性。
例:
#defineload_gdt(dtr)_asm__volatile("lgdt%0"::"m"(*dtr))
#defineswitch_to(prev,next,last)do{ \
unsignedlongesi,edi; \
asmvolatile( "pushl%%ebp\n\t" \
"movl%%esp,%0\n\t" /*saveESP*/ \
"movl%5,%%esp\n\t" /*restoreESP*/
\
"movl$1f,%1\n\t" /*saveEIP*/ \
"pushl%6
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 二手房購(gòu)買(mǎi)協(xié)議書(shū)七篇
- 關(guān)于土地征用協(xié)議書(shū)
- 舞蹈癥病因介紹
- (立項(xiàng)備案申請(qǐng)模板)鋁型材模板項(xiàng)目可行性研究報(bào)告參考范文
- (2024)年產(chǎn)300萬(wàn)噸水穩(wěn)站項(xiàng)目可行性研究報(bào)告寫(xiě)作模板立項(xiàng)備案文件一
- 2024-2025學(xué)年人教版七年級(jí)英語(yǔ)上學(xué)期期末復(fù)習(xí) 專(zhuān)題07 語(yǔ)法填空 【期末必刷15篇】
- 2023年天津市紅橋區(qū)高考語(yǔ)文一模試卷
- 云南省保山市智源初級(jí)中學(xué)2024-2025學(xué)年七年級(jí)上學(xué)期12月月考道德與法治試卷-A4
- 2023年布展裝修項(xiàng)目籌資方案
- 2023年可調(diào)控輥型四輥液壓軋機(jī)項(xiàng)目籌資方案
- 2024年高考語(yǔ)文新課標(biāo)I卷作文“答案與問(wèn)題”講評(píng)
- 籃球球星姚明課件
- 2024年工商聯(lián)副會(huì)長(zhǎng)述職報(bào)告
- 02S515排水檢查井圖集
- 2024-2030年中國(guó)Janus激酶(JAK)抑制劑行業(yè)市場(chǎng)發(fā)展趨勢(shì)與前景展望戰(zhàn)略分析報(bào)告
- 2025高考語(yǔ)文步步高大一輪復(fù)習(xí)講義教材文言文點(diǎn)線(xiàn)面答案精析
- 《工程勘察設(shè)計(jì)收費(fèi)標(biāo)準(zhǔn)》(2002年修訂本)-工程設(shè)計(jì)收費(fèi)標(biāo)準(zhǔn)2002修訂版
- 2024山東能源集團(tuán)中級(jí)人才庫(kù)選拔(高頻重點(diǎn)提升專(zhuān)題訓(xùn)練)共500題附帶答案詳解
- T-CCIIA 0004-2024 精細(xì)化工產(chǎn)品分類(lèi)
- 低年級(jí)革命文化類(lèi)課文教學(xué)探析
- TPM知識(shí)競(jìng)賽題庫(kù)含答案
評(píng)論
0/150
提交評(píng)論