一个操作系统的设计与实现——第12章 任务(三):3特权级任务
特權(quán)級是保護模式的核心概念之一,但我們的操作系統(tǒng)一直沒有引入這個概念。這是因為,特權(quán)級只有在3特權(quán)級任務(wù)存在時才有意義。本章將要實現(xiàn)的是3特權(quán)級任務(wù)的加載與任務(wù)切換。
12.1 特權(quán)級
12.1.1 特權(quán)級的功能
特權(quán)級(Privilege Level),是保護模式中用于限制任務(wù)權(quán)限的機制。特權(quán)級有4級,分別是0~3特權(quán)級。0特權(quán)級權(quán)限最大,供操作系統(tǒng)使用;3特權(quán)級權(quán)限最小,供普通任務(wù)使用。中間的兩級在我們的操作系統(tǒng)中不使用。
特權(quán)級起到的作用如下:
- 在不使用特殊機制的前提下,禁止代碼段寄存器切換特權(quán)級。也就是說,0特權(quán)級任務(wù)只能執(zhí)行0特權(quán)級的代碼,不能執(zhí)行3特權(quán)級的代碼;反之,3特權(quán)級任務(wù)只能執(zhí)行3特權(quán)級的代碼,不能執(zhí)行0特權(quán)級的代碼。這樣,操作系統(tǒng)內(nèi)部的代碼就能得到保護,3特權(quán)級任務(wù)不能隨意使用
 - 在任何情況下,使用數(shù)據(jù)段寄存器只能訪問平級或更低級的數(shù)據(jù)。也就是說,0特權(quán)級任務(wù)能夠訪問0特權(quán)級的數(shù)據(jù),也能訪問3特權(quán)級的數(shù)據(jù);反之,3特權(quán)級任務(wù)只能訪問3特權(quán)級的數(shù)據(jù),不能訪問0特權(quán)級的數(shù)據(jù)。這樣,操作系統(tǒng)內(nèi)部的數(shù)據(jù)就能得到保護,3特權(quán)級任務(wù)不能隨意訪問
 - EFLAGS的第12~13位是IOPL(IO特權(quán)級)位,初值為0。只有特權(quán)級大于等于這兩位,即數(shù)值上小于等于這兩位的代碼段才能執(zhí)行
in和out指令。在我們的操作系統(tǒng)中不修改這兩位,因此,只有0特權(quán)級任務(wù)才有權(quán)限執(zhí)行in和out指令。這樣,IO端口就能得到保護,3特權(quán)級任務(wù)不能隨意訪問 - 有少量非常關(guān)鍵的指令屬于特權(quán)指令,這些指令只有0特權(quán)級任務(wù)有權(quán)限執(zhí)行。在我們的操作系統(tǒng)中,特權(quán)指令包括以下幾種:
- 
hlt指令。顯然,低特權(quán)級任務(wù)不能隨意將CPU掛起 - 
sti、cli指令,以及其他試圖修改IF位的指令,如popf。低特權(quán)級任務(wù)不能關(guān)中斷,否則搶占式任務(wù)切換就失效了 - 試圖修改IOPL的指令,如
popf。低特權(quán)級任務(wù)不能修改IO特權(quán)級,否則對IO端口的保護機制就失效了 - 任何試圖讀取或修改控制寄存器、GDTR、LDTR(局部描述符表,在我們的操作系統(tǒng)中未使用)、IDTR、TR(見下文)的指令。如
mov cr0, eax、lgdt等。這些指令對CPU有重大影響,例如,修改CR0能開關(guān)保護模式和分頁模式,這顯然不能供低特權(quán)級任務(wù)隨意使用 - 
invlpg指令。低特權(quán)級任務(wù)不能干預(yù)TLB 
 - 
 
12.1.2 DPL,RPL與CPL
特權(quán)級由三部分組成:描述符特權(quán)級(Descriptor Privilege Level,DPL),請求特權(quán)級(Requested Privilege Level,RPL)和當(dāng)前特權(quán)級(Current Privilege Level,CPL)。DPL位于描述符中,如段描述符,中斷門等;RPL是段選擇子的低2位;CPL恒等于CS的RPL。
CPL決定了當(dāng)前任務(wù)的執(zhí)行權(quán)限。上文中的"只有0特權(quán)級任務(wù)有權(quán)限執(zhí)行"、"3特權(quán)級任務(wù)只能訪問3特權(quán)級的數(shù)據(jù)"等描述中的特權(quán)級,指的就是CPL。由于CPL恒等于CS的RPL,所以切換CS的過程,就是切換CPL的過程。
DPL決定了一個描述符的最低訪問權(quán)限。向段寄存器加載段選擇子時,CPU要求:在數(shù)值上,CPL <= DPL && RPL <= DPL。即,只有特權(quán)級平級,或更高級的指令才有權(quán)限訪問此描述符。
RPL看上去是個沒什么用的概念。如果沒有RPL,CPL可以恒等于代碼段的DPL;向段寄存器加載段選擇子時,只需要CPL <= DPL即可。然而,RPL解決的是一個比較邊緣的問題。設(shè)想:操作系統(tǒng)給3特權(quán)級任務(wù)提供了一個讀硬盤函數(shù),并要求3特權(quán)級任務(wù)提供數(shù)據(jù)段選擇子以存放結(jié)果。此時,用戶可以想辦法猜到0特權(quán)級數(shù)據(jù)段選擇子,并將其提供給操作系統(tǒng),這樣一來,3特權(quán)級任務(wù)就能讀取0特權(quán)級數(shù)據(jù)了。雖然操作系統(tǒng)可以通過軟件手段檢測任務(wù)提供的段選擇子是否可行,但這樣做很麻煩且效率不高。給段選擇子附加RPL后,即使3特權(quán)級任務(wù)故意把RPL寫成0,操作系統(tǒng)也能在拿到段選擇子后,強制將RPL改成3,再進行后續(xù)操作,從而將特權(quán)級檢查交給CPU進行,即提高了效率,又比較方便。
正因為如此,CPL才由CS的RPL決定,而不是由代碼段描述符的DPL決定。因為DPL只是一個最低標(biāo)準(zhǔn),RPL才能描述真正的特權(quán)級。
12.1.3 特權(quán)級的提升
操作系統(tǒng)存在的意義是為3特權(quán)級任務(wù)提供服務(wù),而不是死守自己的0特權(quán)級代碼,不讓任何人使用。上文提到,數(shù)據(jù)段在任何情況下都不能被低特權(quán)級任務(wù)訪問,但代碼段不同,操作系統(tǒng)可以將一部分函數(shù)開放給3特權(quán)級任務(wù)使用。
然而,操作系統(tǒng)的函數(shù)畢竟是0特權(quán)級的,3特權(quán)級任務(wù)想要使用這些函數(shù),就需要有一套升降級機制,在調(diào)用0特權(quán)級函數(shù)之前先升級,調(diào)用完成后再降級。
對于這個需求,CPU提供的標(biāo)準(zhǔn)機制是調(diào)用門(Call gate),但這種機制要求為每個函數(shù)分別安裝一個調(diào)用門,這是非常麻煩且效率低下的,所以,在我們的操作系統(tǒng)(以及幾乎所有的現(xiàn)代操作系統(tǒng))中都不使用這個機制。
事實上,中斷門也有提升特權(quán)級的能力。因此,中斷門可以用于向3特權(quán)級任務(wù)提供0特權(quán)級函數(shù)。具體步驟如下:
- 發(fā)起一個中斷
 - 檢查
CPL <= DPL(中斷沒有RPL)。如果通過,則允許任務(wù)進入中斷門。只有由指令發(fā)起的中斷會進行這一步,外中斷和CPU自己發(fā)起的中斷無視中斷門的DPL - 檢查
CPL >= 中斷門中CS的RPL,如果通過,則允許CS升級并調(diào)用中斷門中的函數(shù) 
綜上,想要使用中斷門,就必須滿足兩個條件:
- 有權(quán)限進入中斷門。這由中斷門的DPL決定。這樣做的目的是對3特權(quán)級任務(wù)能夠使用的中斷門進行限制
 - 進入中斷門后,必須發(fā)生升級或平級,不允許降級
 
12.1.4 0特權(quán)級棧、TSS與TSS描述符
在特權(quán)級切換時,CPU還有一個特殊要求:SS的RPL必須時刻等于CPL。這意味著,每個3特權(quán)級任務(wù)必須有兩個棧,分別供0特權(quán)級與3特權(quán)級使用。當(dāng)特權(quán)級發(fā)生切換時,棧也要跟著切換。那么,這兩個棧存放在哪呢?
CPU規(guī)定:0特權(quán)級棧需要放置在任務(wù)狀態(tài)段(Task State Segment,TSS)中。TSS是一個至少為104字節(jié)(見下文)的表,結(jié)構(gòu)如下:
TSS需要以TSS描述符的形式安裝在GDT中。TSS描述符是系統(tǒng)段的一種,結(jié)構(gòu)如下:
TSS描述符中的B位,即忙(Busy)位,由CPU在加載TSS時自動置1,構(gòu)造TSS描述符時應(yīng)將其置0。其他位的含義同段描述符。
與GDT、IDT類似,CPU也為TSS提供了一個專用寄存器。不過,這個寄存器不叫TSSR,而是叫任務(wù)寄存器(Task Register,TR)。在TSS描述符安裝到GDT中以后,需要使用ltr TSS描述符的選擇子指令將TSS加載到TR。TSS描述符的選擇子可以存放在16位寄存器或內(nèi)存中。
TSS(以及任務(wù)門,Task gate)是CPU提供的用于任務(wù)切換的標(biāo)準(zhǔn)機制,然而還是老問題:TSS使用起來非常麻煩,需要給每個任務(wù)都安裝一個,且效率很低。所以,在我們的操作系統(tǒng)(以及幾乎所有的現(xiàn)代操作系統(tǒng))中都不使用這個機制。但TSS還有另一個功能,那就是獲取任務(wù)的0特權(quán)級棧。具體來說,當(dāng)中斷發(fā)生時,引入特權(quán)級概念后的過程如下:
- 檢查CPL是否有權(quán)限進入中斷門
 - 檢查中斷門中的CS是否能使CPL升級或平級
 - 如果中斷門中的CS與CPL平級,跳過此步驟;否則,暫存SS和ESP,然后將SS和ESP分別切換為TSS中的SS0和ESP0,再將暫存的SS通過高位補0的方式填充至32位后壓棧,接著將暫存的ESP壓棧
 - 將EFLAGS壓棧,然后將EFLAGS的IF位清零
 - 將CS通過高位補0的方式填充至32位后壓棧
 - 將EIP壓棧
 - 跳轉(zhuǎn)至中斷門中的中斷處理函數(shù)
 
也就是說,雖然不使用TSS進行任務(wù)切換,但仍然需要一個TSS,其存在的唯一目的就是提供0特權(quán)級棧。
上文提到,TSS"至少為104字節(jié)"。這是因為TSS還能提供一個被稱為IO位圖的功能,這個位圖延長在TSS后面,由TSS中的IO位圖基址控制。IO位圖用于越過IOPL,給特定的一些IO端口開白名單。在我們的操作系統(tǒng)中不使用IO位圖,但也不能將其置0。CPU要求:如果IO位圖基址的值大于等于TSS描述符中的TSS限長,則表示IO位圖不存在。在我們的操作系統(tǒng)中,可將其置103(或0xff等更大的值)。
12.1.5 特權(quán)級的降低
3特權(quán)級任務(wù)能夠通過中斷門提升特權(quán)級,但這畢竟是暫時的。在中斷處理函數(shù)調(diào)用完成后,就需要回到3特權(quán)級。
特權(quán)級的降低由iret指令實現(xiàn)。引入特權(quán)級概念后,該指令的執(zhí)行過程如下:
- 從棧中依次彈出EIP、CS、EFLAGS。如果彈出的CS的RPL為0,
iret指令就此完成;否則,繼續(xù)執(zhí)行以下步驟 - 依次檢查DS、ES、FS、GS的RPL是否為3。如果不是,則將其修改為0。這一步的目的是:避免因中斷返回使0特權(quán)級的段選擇子泄漏到3特權(quán)級。GDT的第一個描述符必須為空的目的就在于此
 - 繼續(xù)從棧中彈出ESP和SS,將棧恢復(fù)到3特權(quán)級棧。因此,TSS中不需要存放3特權(quán)級棧,3特權(quán)級棧的SS和ESP位于0特權(quán)級棧中
 
12.2 3特權(quán)級任務(wù)的實現(xiàn)原理
12.2.1 3特權(quán)級代碼段與數(shù)據(jù)段
GDT中需要安裝一個3特權(quán)級代碼段描述符,和一個3特權(quán)級數(shù)據(jù)段描述符,以供3特權(quán)級任務(wù)使用。這兩個描述符除了DPL為3外,其他屬性與0特權(quán)級描述符相同。
12.2.2 TSS
GDT中需要安裝一個TSS描述符,并使用ltr指令加載這個TSS。
12.2.3 3特權(quán)級任務(wù)的切換
3特權(quán)級任務(wù)的切換也基于時鐘中斷。但上一章中的"6個段寄存器不會發(fā)生改變"這一結(jié)論現(xiàn)在已經(jīng)不成立了。所以,在時鐘中斷處理函數(shù)中,不僅需要將8個通用寄存器壓棧,還需要將除了CS和SS以外的4個段寄存器壓棧。
此外,由于每個任務(wù)都有一個ESP0,所以在任務(wù)切換時,需要修改TSS中的ESP0。SS0對于每個任務(wù)來說都是一樣的,所以無需修改。
12.2.4 3特權(quán)級任務(wù)的創(chuàng)建
與0特權(quán)級任務(wù)類似,3特權(quán)級任務(wù)的創(chuàng)建也基于偽造棧技術(shù)。
現(xiàn)在,由于4個段寄存器也被壓棧,對于0特權(quán)級任務(wù)來說,需要在棧上偽造15個寄存器的值;對于3特權(quán)級任務(wù)來說,需要在棧上偽造17個寄存器的值。
3特權(quán)級任務(wù)不僅需要0特權(quán)級棧,還需要3特權(quán)級棧。在我們的操作系統(tǒng)中,3特權(quán)級棧為一頁,其虛擬地址固定為0xc0000000向下的0x1000字節(jié)。
12.2.5 3特權(quán)級任務(wù)的加載與重定位
3特權(quán)級任務(wù)往往不是操作系統(tǒng)的一部分,而是由用戶提供的,存放在硬盤上的一個程序。從硬盤上加載程序可由硬盤驅(qū)動完成,解析ELF也不是難事,剩下的問題是:這個程序該如何重定位呢?
平坦模型失去了重定位能力,重定位由分頁模式以一種完全不同的方式實現(xiàn)。具體來說,編譯器可以為程序提供一套虛擬地址,只要虛擬地址落在任務(wù)地址空間內(nèi)即可。操作系統(tǒng)在加載任務(wù)時,不主動分配虛擬地址,而是使用ELF文件提供的虛擬地址,并為這些虛擬地址分配物理地址,并安裝PDE、PTE,然后,將ELF文件中的程序段展開到這些虛擬地址中。
12.3 3特權(quán)級任務(wù)的實現(xiàn)
12.3.1 添加3特權(quán)級描述符、TSS描述符
請看本章代碼12/Mbr.s。
第143~145行,定義了三個新的段描述符。分別是3特權(quán)級代碼段描述符,段選擇子是(3 << 3) | 0x3;3特權(quán)級數(shù)據(jù)段描述符,段選擇子是(4 << 3) | 0x3;TSS描述符,段選擇子是5 << 3。
TSS定義在內(nèi)核中,在MBR中不知道其地址,所以,TSS描述符目前僅用于占位。
12.3.2 修改時鐘中斷處理函數(shù)
請看本章代碼12/Int.s。
第3行,聲明了外部鏈接的printStr函數(shù)。
第7行,聲明了外部鏈接的TSS。TSS定義在本章代碼12/Task.hpp中。
第108~111行, 將四個數(shù)據(jù)段寄存器壓棧。
第137~138行,將TSS中的ESP0修改為新任務(wù)的ESP0。
第141~144行,彈出新任務(wù)的四個數(shù)據(jù)段寄存器。
12.3.3 系統(tǒng)調(diào)用
上文提到,可以使用中斷將0特權(quán)級函數(shù)提供給3特權(quán)級任務(wù)使用。這個方案看似需要很多中斷號,但實際上有更好的設(shè)計:構(gòu)造一個函數(shù)表,其中存放的是操作系統(tǒng)為3特權(quán)級任務(wù)提供的函數(shù)。然后,只使用一個中斷,在中斷處理函數(shù)中調(diào)用函數(shù)表中的函數(shù),具體調(diào)用哪個函數(shù)由調(diào)用者通過一個索引值指定。上述操作被稱為系統(tǒng)調(diào)用,調(diào)用者提供的索引值被稱為系統(tǒng)調(diào)用號。
在我們的操作系統(tǒng)中,調(diào)用者應(yīng)使用EAX存放系統(tǒng)調(diào)用號;EBX、ECX、EDX用于存放參數(shù)(如果有)。在系統(tǒng)調(diào)用的中斷處理函數(shù)中,不管有幾個參數(shù),都將EBX、ECX、EDX壓棧,然后使用EAX找到一個函數(shù)并調(diào)用。
請看本章代碼12/Int.s。
intSyscall是系統(tǒng)調(diào)用的中斷處理函數(shù)。
第150~152行,不管實際需要幾個參數(shù),都將EBX、ECX、EDX壓棧。
第153行,使用EAX中存放的系統(tǒng)調(diào)用號調(diào)用[syscallList + eax * 4]這個函數(shù)。
第154行,將棧恢復(fù)。
第156行,使用iret指令從中斷返回。
第210行,在intList的最后添加intSyscall函數(shù)。現(xiàn)在的IDT擴充至49個中斷門,系統(tǒng)調(diào)用的中斷號是0x30。
第212~213行,定義系統(tǒng)調(diào)用表。目前只支持一個系統(tǒng)調(diào)用:printStr函數(shù),其系統(tǒng)調(diào)用號為0。
接下來,請看本章代碼12/Int.hpp。
第7行,將IDT的大小修改為49。
第25行,將intSyscall函數(shù)安裝到IDT中。注意:由于系統(tǒng)調(diào)用是給3特權(quán)級任務(wù)使用的,所以中斷門的DPL必須為3,這樣3特權(quán)級任務(wù)才有權(quán)限使用這個中斷門。
12.3.4 內(nèi)存管理系統(tǒng)的修改
請看本章代碼12/Memory.h。
第9行,聲明了installTaskPage函數(shù)。
接下來,請看本章代碼12/Memory.hpp。
installTaskPage函數(shù)是本章新增的函數(shù),其用于在指定的虛擬地址處分配物理地址,并將虛擬地址與物理地址建立聯(lián)系。所以,這個函數(shù)相當(dāng)于__allocatePage函數(shù)的簡化版。
第85行,從TCB中取得當(dāng)前任務(wù)的虛擬地址位圖,接下來需要手動設(shè)置這個位圖。
第87行,判斷虛擬地址是否超出了位圖范圍。進行這個判斷的原因是,這個函數(shù)不僅用于安裝ELF文件中的地址,這些地址都很接近0;還用于安裝3特權(quán)級棧,地址是0xc0000000 - 0x1000,這個地址遠遠超過了一頁位圖能表示的128M內(nèi)存,所以,此時無需設(shè)置位圖。
第89~92行,根據(jù)虛擬地址設(shè)定位圖中已使用的位。
第95~100行,為虛擬地址的每一頁安裝物理地址。這段代碼的實現(xiàn)原理和第66~71行一致。
12.3.5 0特權(quán)級任務(wù)加載器的修改
請看本章代碼12/Task.hpp。
由于任務(wù)切換時添加了4個段寄存器的壓棧,所以偽造棧的過程也要隨之修改。
第96行,將原先的11 * 4修改為15 * 4。
第108~114行,重新調(diào)整棧上偽造的數(shù)據(jù)。
12.3.6 安裝并加載TSS
請看本章代碼12/Task.h。
第16行,聲明了外部鏈接的TSS。
接下來,請看本章代碼12/Task.hpp。
第10行,定義了TSS,但暫時沒有初始化。
__makeTSSDescriptor函數(shù)用于構(gòu)造TSS描述符。
讀者要格外小心這個函數(shù)的實現(xiàn),例如"TSS地址的最高8位",從TSS描述符的結(jié)構(gòu)圖上看,其位于第56~63位,但如果實現(xiàn)為(tssBase & 0xff000000) << 56,那就完全錯了。tssBase & 0xff000000得到的這個數(shù)字相當(dāng)于已經(jīng)左移了24位,所以,只需要再左移32位即可。
__installTSS函數(shù)用于安裝TSS。
第23~24行,初始化TSS中的SS0與IO位圖基址。內(nèi)核用不到ESP0,所以無需初始化。
第28行,使用sgdt指令獲取GDTR。
第30行,在GDT中安裝TSS描述符。
第32行,重新加載GDT。此時,TSS的段選擇子5 << 3可用。
第33行,使用ltr指令加載TSS。
第51行,在taskInit函數(shù)中添加對__installTSS函數(shù)的調(diào)用。
12.3.7 3特權(quán)級任務(wù)的加載
請看本章代碼12/Task.h。
第23行,聲明了loadTaskPL3函數(shù)。
接下來,請看本章代碼12/Task.hpp。
loadTaskPL3函數(shù)是本章新增的函數(shù),其用于加載3特權(quán)級任務(wù)。
不同于loadTaskPL0函數(shù),loadTaskPL3函數(shù)的參數(shù)是硬盤的起始扇區(qū)號與扇區(qū)數(shù)。所以,此函數(shù)可以一步到位的從硬盤上加載3特權(quán)級任務(wù)。
第124~136行,與loadTaskPL0函數(shù)的開頭部分一致。用于分配新任務(wù)的TCB,CR3,以及虛擬地址位圖,并設(shè)置好新的CR3。
第138行,將扇區(qū)數(shù)轉(zhuǎn)換為頁數(shù)。這里使用了以下公式:
\[\lceil \frac{a}{b} \rceil =\lfloor \frac{a\,\,+\,\,b\,\,-\,\,1}{b} \rfloor \]第139行,分配ELF緩沖區(qū)。
第141行,調(diào)用硬盤驅(qū)動中的讀硬盤函數(shù),將任務(wù)從硬盤讀取到緩沖區(qū)。
第143~145行,讀取ELF文件頭中的3個信息:程序頭表地址,程序頭表中每個表項的大小,以及程序頭表中表項的數(shù)量。
第147~154行,保存當(dāng)前的CR3,并將其暫時切換到新任務(wù)的CR3上。這里的內(nèi)聯(lián)匯編使用了第6章討論的獨占約束"=&r"。這一步的目的是:任務(wù)加載到的虛擬地址屬于任務(wù)自己的地址空間,所以,應(yīng)在任務(wù)的CR3中分配內(nèi)存。
第156行,遍歷程序頭表中的每個表項。
第158行,判斷當(dāng)前表項的類型,只關(guān)注類型為1的表項。
第160行,從表項中讀取源地址。
第161行,從表項中讀取目的地址。
第163行,將ELF要求的內(nèi)存大小向上取整到頁數(shù)。這里使用了上文中的公式。
第165行,使用installTaskPage函數(shù)在ELF要求的加載地址處安裝頁。
第167~168行,加載程序段并構(gòu)造BSS段。
第172行,從ELF文件頭中讀取任務(wù)的入口點。
至此,ELF已經(jīng)加載完畢。
第174行,回收ELF緩沖區(qū)。這里雖然使用的是任務(wù)的CR3,但內(nèi)核地址空間是共享的,只要有權(quán)限,任何CR3都能分配和回收內(nèi)核頁。
第176~196行,偽造3特權(quán)級任務(wù)的0特權(quán)級棧,一共需要偽造17個寄存器的值。
第198行,為3特權(quán)級棧安裝物理頁。
第200~204行,將CR3換回。
第206~208行,初始化新任務(wù)的虛擬地址位圖,然后將其添加到任務(wù)隊列中。這兩行代碼與loadTaskPL0函數(shù)中的一致。
12.4 測試
請看本章代碼12/Test.c。
這個任務(wù)現(xiàn)在運行在3特權(quán)級下,所以其沒有權(quán)限使用操作系統(tǒng)中的所有函數(shù),唯一能用的是0號系統(tǒng)調(diào)用。使用系統(tǒng)調(diào)用還有一個好處:由于系統(tǒng)調(diào)用是中斷,而中斷過程中不會發(fā)生任務(wù)切換,所以系統(tǒng)調(diào)用是自帶鎖的。
第5~9行,在循環(huán)中不斷發(fā)起0號系統(tǒng)調(diào)用,打印Task字符串。
由于我們的操作系統(tǒng)目前仍然不支持任務(wù)回收,所以任務(wù)不能退出。
接下來,請看本章代碼12/Makefile。
第6行,編譯Test.c。
第8行,鏈接Test.o。
對于ld命令來說,如果不設(shè)定-Ttext-segment參數(shù),則任務(wù)的默認(rèn)加載地址為0x8048000。我們不需要也不能使用這么大的加載地址,因為這個數(shù)字甚至超過了128M。因此,鏈接Test.o時需要加上-Ttext-segment 0參數(shù)。
第11行,將Test寫入虛擬硬盤。
接下來,請看本章代碼12/Kernel.c。
第15~16行,將測試任務(wù)加載兩次。
第22~26行,在循環(huán)中不斷發(fā)起0號系統(tǒng)調(diào)用,打印Kernel字符串。這樣做有兩個目的:
- 利用系統(tǒng)調(diào)用自帶鎖這一性質(zhì)
 - 觀察中斷門的平級調(diào)用
 
12.5 調(diào)試
本章的任務(wù)運行在3特權(quán)級下,因此,如果想要手動進行任務(wù)切換以調(diào)試程序,就需要將__installIDT函數(shù)中的0x8e00臨時修改為0xee00,使得3特權(quán)級任務(wù)也能進入0x20中斷門。
在bochs調(diào)試器中,TSS可以通過info tss命令查看。
總結(jié)
以上是生活随笔為你收集整理的一个操作系统的设计与实现——第12章 任务(三):3特权级任务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Python标准库中隐藏的利器
 - 下一篇: 华为全场景新品亮相 2023 数字科技生