国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
內(nèi)核代碼學(xué)習(xí)==>深入介紹Linux內(nèi)核(七)

深入介紹Linux內(nèi)核(七)



.......續(xù)上第六篇


4.7 任務(wù)管理

任務(wù)(Task)是處理器可以分配調(diào)度、執(zhí)行和掛起的一個(gè)工作單元。它循
於執(zhí)行程式、任務(wù)或進(jìn)程、作業(yè)系統(tǒng)服務(wù)、中斷或異常處理行程和內(nèi)核代碼?。

80X86提供了一種機(jī)制,這種機(jī)制可用來保存任務(wù)的狀態(tài)、分派任務(wù)執(zhí)行以及從一個(gè)任務(wù)切換到另一個(gè)任務(wù)。當(dāng)工作在保護(hù)模式下,處理器所有執(zhí)行都在任務(wù)中。即使是簡(jiǎn)單系統(tǒng)也必須起碼定義一個(gè)任務(wù)。更為復(fù)雜的系統(tǒng)可以使用處理器的任務(wù)管理功能來支援多工應(yīng)用。

80X86提供了多工的硬體支援。任務(wù)是一個(gè)正在執(zhí)行的程式,或者是一個(gè)等待準(zhǔn)備執(zhí)行的程式。透過中斷、異常、跳轉(zhuǎn)或呼叫,我們可以執(zhí)行一個(gè)任務(wù)。當(dāng)這些控制轉(zhuǎn)移形式之一和某個(gè)描述符號(hào)表中指定項(xiàng)的內(nèi)容一起使用時(shí),那麼這個(gè)描述符號(hào)就是一類導(dǎo)致新任務(wù)開始執(zhí)行的描述符號(hào)。描述符號(hào)表中與任務(wù)相關(guān)的描述符號(hào)有兩類:任務(wù)狀態(tài)段描述符號(hào)和任務(wù)門。當(dāng)執(zhí)行權(quán)傳給這任何一類描述符號(hào)時(shí),都會(huì)造成任務(wù)切換。


任務(wù)切換很像程序呼叫,但任務(wù)切換會(huì)保存更多的處理器狀態(tài)資訊。任務(wù)切換會(huì)把控制權(quán)完全轉(zhuǎn)移到一個(gè)新的執(zhí)行環(huán)境,即新任務(wù)的執(zhí)行環(huán)境。這種轉(zhuǎn)移操作要求保存處理器中幾乎所有寄存器的當(dāng)前內(nèi)容,包括標(biāo)志寄存器EFLAGS和所有段積存器。與行程不過,任務(wù)不可重入。任務(wù)切換不會(huì)把任何資訊壓入堆棧中,處理器的狀態(tài)資訊都被保存在記憶體中稱為任務(wù)狀態(tài)段(Task state segment)的資料結(jié)構(gòu)中。


4.7.1 任務(wù)的結(jié)構(gòu)和狀態(tài)

一個(gè)任務(wù)由兩部分構(gòu)成:任務(wù)執(zhí)行空間和任務(wù)狀態(tài)段TSS(Task-state segment)。任務(wù)執(zhí)行空間包括代碼段、堆棧段和一個(gè)或多個(gè)資料段,見圖4-33所示。如果作業(yè)系統(tǒng)使用了處理器的特權(quán)級(jí)保護(hù)機(jī)制,那麼任務(wù)執(zhí)行空間就需要為每個(gè)特權(quán)級(jí)提供一個(gè)獨(dú)立的堆??臻g。TSS指定了構(gòu)成任務(wù)執(zhí)行空問的各個(gè)段,並且為任務(wù)狀態(tài)資訊提供儲(chǔ)存空間。在多工環(huán)境中,TSS也為任務(wù)之間的鏈結(jié)提供了處理方法。






一個(gè)任務(wù)使用指向其TSS的段選擇符號(hào)來指定。當(dāng)一個(gè)任務(wù)被載入進(jìn)處理器中執(zhí)行時(shí),那麼該任務(wù)的段選擇符號(hào)、基底位址、段限長(zhǎng)以及TSS段描述符號(hào)屬性就會(huì)被載入進(jìn)任務(wù)寄存器TR(Task Register)中。如果使用了分頁機(jī)制,那麼任務(wù)使用的頁目錄表基底位址就會(huì)被載入進(jìn)控制寄存器CR3中。當(dāng)前執(zhí)行任務(wù)的狀態(tài)由處理器所有以下一些內(nèi)容組成:


▓ 所有通用寄存器和段寄存器資訊:

▓ 標(biāo)志寄存器EFLAGS、程式指標(biāo)EIP、控制寄存器CR3、任務(wù)寄存器和LDTR寄存器:

▓ 段寄存器指定的任務(wù)當(dāng)前執(zhí)行空間:

▓ I/O映射點(diǎn)陣圖基底位址和I/O點(diǎn)陣圖資訊 (在TSS中) ;

▓ 特權(quán)級(jí)0、1和2的堆棧指標(biāo) (在TSS中);

▓ 鏈結(jié)至前一個(gè)任務(wù)的鏈指標(biāo) (在TSS中)。


4.7.2 任務(wù)的執(zhí)行

軟件或處理器可以使用以下方法之一來調(diào)度執(zhí)行一個(gè)任務(wù):

▓ 使用CALL指令明確地呼叫一個(gè)任務(wù);

▓ 使用JMP指令明確地跳轉(zhuǎn)到一個(gè)任務(wù) (Linux內(nèi)核使用的方式) ;

▓ (由處理器) 隱含地呼叫一個(gè)中斷控制碼處理任務(wù);

▓ 隱含地呼叫一個(gè)異??刂拼a處理任務(wù);

所有這些排程調(diào)度任務(wù)執(zhí)行的方法都會(huì)使用一個(gè)指向任務(wù)門或任務(wù)TS段的選擇符號(hào)來確定一個(gè)任務(wù)。當(dāng)使用CALL或JMP指令調(diào)度一個(gè)任務(wù)時(shí),指令中的選擇符號(hào)既可以直接選擇任務(wù)的TSS,也可以選擇存放有TSS選擇符號(hào)的任務(wù)門。當(dāng)調(diào)度一個(gè)任務(wù)來處理一個(gè)中斷或異常時(shí),那麼IDT中該中斷或異常表項(xiàng)必須是一個(gè)任務(wù)門,並且其中含有中斷或異常處理任務(wù)的TSS選擇符號(hào)。

當(dāng)調(diào)度排程一個(gè)任務(wù)執(zhí)行時(shí),當(dāng)前正在執(zhí)行任務(wù)和調(diào)度任務(wù)之間會(huì)自動(dòng)地發(fā)生任務(wù)切換操作。在任務(wù)切換期間,當(dāng)前執(zhí)行任務(wù)的執(zhí)行環(huán)境 (稱為任務(wù)的狀態(tài)或上下文)會(huì)被保存到它的TSS中並且暫停該任務(wù)的執(zhí)行。此后新調(diào)度任務(wù)啊下文會(huì)被載入進(jìn)處理器中,並且從載入的EIP指向的指令處開始執(zhí)行新任務(wù)。

如果當(dāng)前執(zhí)行任務(wù)(呼叫者) 呼叫了被調(diào)度的新任務(wù)(被呼叫者),那么呼叫者的TSS段選擇符號(hào)會(huì)被保存在被呼叫者TSS中,從而提供了一個(gè)返回呼叫者的鏈結(jié)。對(duì)於所有80X86處理器,任務(wù)是不可遞回呼叫的,即任務(wù)不能呼叫或跳轉(zhuǎn)到自己。

中斷或異??梢酝高^切換到一個(gè)任務(wù)來進(jìn)行處理。在這種情況下,處理器不僅能夠執(zhí)行任務(wù)切換來處理中斷或異常,而且也會(huì)在中斷或異常處理任務(wù)返回時(shí)自動(dòng)地切換回被中斷的任務(wù)中去。這種操作方式可以處理在中斷任務(wù)執(zhí)行時(shí)發(fā)生的中斷。

作為任務(wù)切換操作的一部份,處理器也會(huì)切換到另一個(gè)LDT,從而允許每個(gè)任務(wù)對(duì)基於LDT的段具有不同邏輯到實(shí)體位址的映射。同時(shí),頁目錄寄存器CR3也會(huì)在切換時(shí)被重新載入,因此每個(gè)任務(wù)可以有自己的一套頁表。這些保護(hù)措施能夠用來隔絕各個(gè)任務(wù)並且防止它們相互干擾。

使用處理器的任務(wù)管理功能來處理多工應(yīng)用足任選的。我們也可以使用軟件來實(shí)現(xiàn)多工,使得每個(gè)軟體定義的任務(wù)在一個(gè)80X86體系結(jié)構(gòu)的任務(wù)上下文中執(zhí)行。
4.7.3任務(wù)管理資料結(jié)構(gòu)

處理器定義了一下一些支援多工的寄存器和資料結(jié)構(gòu):

▓ 任務(wù)狀態(tài)段TSS;

▓ TSS描述符號(hào);

▓ 任務(wù)暫存器TR;

▓ 任務(wù)門描述符號(hào);

▓ 標(biāo)志寄存器EFLAGS中的NT標(biāo)志。

使用這些資料結(jié)構(gòu),處理器可以從一個(gè)任務(wù)切換到另一個(gè)任務(wù),同時(shí)保存原任務(wù)的的上下文,以允許任務(wù)重新執(zhí)行。


任務(wù)狀態(tài)段

用於恢復(fù)一個(gè)任務(wù)執(zhí)行的處理器狀態(tài)資訊被保存在一類稱為任務(wù)狀態(tài)段TSS(Task state segment) 的段中。圖4-34給出了32位元CPU使用的TSS的格式。TSS段中各欄位可分成兩大類:動(dòng)態(tài)欄位和靜態(tài)欄位。







⑴ 動(dòng)態(tài)欄位。當(dāng)任務(wù)切換而被掛起時(shí),處理器會(huì)更新動(dòng)態(tài)欄位的內(nèi)容。這些欄位包括:

● 通用寄存器欄位。用于保存EAX、ECX、EDX、EBX、ESP、EBP、ESI和EDI寄存器的內(nèi)容。

● 段選擇符號(hào)欄位。用于保存ES、CS、SS、DS、FS和GS段寄存器的內(nèi)容。

● 堆棧寄存器EFLAGS欄位。在切換之間保存EFLAGS。

● 指令指標(biāo)EIP欄位。在切換之前EIP寄存器內(nèi)容。

● 先前任務(wù)連接欄位。含有前一個(gè)任務(wù)TSS段選擇符號(hào)(在呼叫、中斷或異常激發(fā)的任務(wù)切換時(shí)更新)。該欄位(通常也稱為后連接欄位(Back link field))允許任務(wù)使用IRET指令切換到前一個(gè)任務(wù)。








⑵ 靜態(tài)欄位。處理器會(huì)讀取靜態(tài)欄位的內(nèi)容,但通常不會(huì)改變它們。這些欄位內(nèi)容是在任務(wù)被建立時(shí)設(shè)置的。這些欄位有:

● LDT段選擇符號(hào)欄位。含有仟?jiǎng)?wù)的LDT段的選擇符號(hào)。

● CR3控制暫存器欄位。含行任務(wù)使用的頁目錄物理基底位址。控制寄存器CR3通常也被稱為頁目錄基底位址:寄存器PDBR(Page directory base register)。

● 特權(quán)級(jí)0、l和2的堆棧指標(biāo)欄位。這些堆棧指標(biāo)由堆棧段選擇符號(hào)(SS0、SS1和SS2)和堆棧中偏移量指標(biāo)(ESP0、ESPl和ESP2)組成。注意,對(duì)於指定的一個(gè)任務(wù),這些欄位的值是不變的。岡此,如果任務(wù)中發(fā)生堆棧切換,寄存器SS和ESP的內(nèi)容將會(huì)改變。

● 除錯(cuò)陷阱(Debug Trap)T標(biāo)志欄位。該欄位位于位元組0x64 Bit0處。當(dāng)設(shè)置了該位時(shí),處理器切換到該任務(wù)的操作將產(chǎn)生一個(gè)錯(cuò)異常。

● I/O點(diǎn)陣固基底位址欄位。該欄位含有從TSS段開始處到I/O許可點(diǎn)陣圖處的16位偏栘值。

如果使用了分頁機(jī)制,那麼在任務(wù)切換期間應(yīng)該避免處理器操作的TSS段中(前104位元組中)含有記憶體頁邊界。如果TSS這部分包含記憶體頁邊界,那麼該邊界處兩邊的頁面都必須同時(shí)并且連續(xù)存于記憶體中。另外,如果使用了分頁機(jī)制,那麼與原任務(wù)TSS和新任務(wù)TSS相關(guān)的頁面,以及對(duì)應(yīng)的描述符號(hào)表表項(xiàng)應(yīng)該是可讀寫的。


TSS描述符號(hào)

與其他段一樣,任務(wù)狀態(tài)段TSS也是使用段描述符號(hào)來定義。圖4-35給出了TSS描述符號(hào)的格式。TSS描述符號(hào)只能存放在GDT中。







類型欄位TYPE中的忙碌標(biāo)志B用於指明任務(wù)是否處?kù)睹顟B(tài)。忙狀態(tài)的任務(wù)是當(dāng)前正在執(zhí)行的任務(wù)或等待執(zhí)行(被掛起)的任務(wù)。值為0b1001的類型欄位表明任務(wù)處?kù)斗腔顒?dòng)狀態(tài):而值為0b1011的類型欄位表示任務(wù)正忙。任務(wù)是不可以遞回執(zhí)行的,因此處理器使用忙標(biāo)志B來檢測(cè)任何企圖對(duì)被中斷執(zhí)行任務(wù)的呼叫。

其中基底位址、段限長(zhǎng)、描述符號(hào)特權(quán)級(jí)DPL、顆粒度G和存在位具有與資料段描述符號(hào)中相應(yīng)欄位同樣的功能。當(dāng)G=0時(shí),限長(zhǎng)欄位必須具有等於或大於103(0x67)的值,即TSS段的最小長(zhǎng)度不得小於104位元組。如果TSS段中還包含I/O許可點(diǎn)陣圖,那麼,TSS段長(zhǎng)度需要大一些。另外,如果作業(yè)系統(tǒng)還想在TSS段中存放其他一些資訊,那麼TSS段就需要更大的長(zhǎng)度。

使用呼叫或跳轉(zhuǎn)指令,任何可以存取TSS描述符號(hào)的程式都能夠造成任務(wù)切換。可以存取TSS描述符號(hào)的程式其CPL數(shù)值必須小於或等於TSS描述符號(hào)的DPL。在大多數(shù)系統(tǒng)中,TSS描述符號(hào)的DPL欄位值應(yīng)該設(shè)置成小於3。這樣,只有具有特權(quán)級(jí)的軟體可以執(zhí)行任務(wù)切換操作。然而在多工應(yīng)用中,某些TSS的DPL可以設(shè)置成3,以使得在用戶特權(quán)級(jí)上也能進(jìn)行任務(wù)切換操作。

可存取一個(gè)TSS段描述符號(hào)並沒有給程式讀寫該描述符號(hào)的能力。若想讀或修改一個(gè)TSS段描述符號(hào),可以使用映射到記憶體相同位置的資料段描述符號(hào)(即別名描述符號(hào))來操作。把TSS描述符號(hào)載入進(jìn)任何段寄存器將導(dǎo)致一個(gè)異常。企圖使用Tl標(biāo)志置位元的選擇符號(hào)(即當(dāng)前LDT中的選擇符號(hào))來存取TSS段也將導(dǎo)致異常。


任務(wù)寄存器

任務(wù)暫存器TR(Task Register)中存放著16位元的段選擇符號(hào)以及當(dāng)前任務(wù)TSS段的整個(gè)描述符號(hào)(不可見部分)。這些資訊是從GDT中當(dāng)前任務(wù)的TSS描述符號(hào)中復(fù)制過來的。處理器使用任務(wù)寄存器TR的不可見部分來緩沖TSS段描述符號(hào)內(nèi)容。

指令LTR和STR分別用於載入和保存任務(wù)寄存器的可見部分,即TSS段選擇符號(hào)。LTR指令只能被特權(quán)級(jí)。的程式執(zhí)行。LTR指令通常用於系統(tǒng)初始化期間給TR寄存器載入初值(例如,任務(wù)0的、TSS段選擇符號(hào)),隨后在系統(tǒng)執(zhí)行期間,TR的內(nèi)容會(huì)在任務(wù)切換時(shí)自動(dòng)地被改變。


任務(wù)門描述符號(hào)

任務(wù)門描述符號(hào)(Task gate descriptor)提供對(duì)一個(gè)任務(wù)間接、受保護(hù)地的真的參照引用,其格式見圖所示。任務(wù)描述符號(hào)可以被存放在GDT、LDT或IDT表中。

任務(wù)描述符號(hào)中的TSS選擇符號(hào)欄位指向GDT中的一個(gè)TSS段描述符號(hào)。這個(gè)TSS選擇符號(hào)欄位中的RPL域不用。任務(wù)描述符號(hào)中的DPL用於在任務(wù)與換時(shí)控制對(duì)TSS段的存取。當(dāng)程式透過任務(wù)門呼叫或跳轉(zhuǎn)到一個(gè)任務(wù)時(shí),程式的CPL以及指向任務(wù)門的門選擇符號(hào)的RPL值必須小於或等於任務(wù)門描述符號(hào)中的DPL。請(qǐng)注意,當(dāng)使用任務(wù)門時(shí),目標(biāo)TSS段描述符號(hào)的DPL忽略不用。

程式可以透過任務(wù)門描述符號(hào)或者TSS段描述符號(hào)來存取一個(gè)任務(wù)。圖4-36示出了LDT、GDT和IDT表中的任務(wù)門如何都指向同一個(gè)任務(wù)。








4.7.4任務(wù)切換

處理器可使用一下4種方式之一執(zhí)行任務(wù)切換操作:

⑴ 當(dāng)前任務(wù)對(duì)GDT中的TSS描述符號(hào)執(zhí)行JMP或CALL指令;

⑵ 當(dāng)前任務(wù)對(duì)GDT或LDT中的任務(wù)門描述符號(hào)執(zhí)行JMP或CALL指令;

⑶ 中斷或異常向量指向IDT表中的任務(wù)門描述符號(hào);

⑷ 當(dāng)EFLAGS中的NT標(biāo)志設(shè)置位元時(shí)當(dāng)前任務(wù)執(zhí)行IRET指令。


JMP、CALL和IRET指令以及中斷和異常都是處理器的普通機(jī)制,可用于不發(fā)生任務(wù)切換的環(huán)境中。對(duì)於TSS描述符號(hào)或任務(wù)門的引用(當(dāng)呼叫或跳轉(zhuǎn)駕一個(gè)任務(wù)) ,或者NT標(biāo)志的狀態(tài)(當(dāng)執(zhí)行IRET指令時(shí))確定了是否會(huì)發(fā)生任務(wù)切換。

進(jìn)行任務(wù)切換時(shí),JMP或CALL指令能夠把控制轉(zhuǎn)移到TSS描述符號(hào)或任務(wù)門上。使用這兩種方式的作用相同,都會(huì)導(dǎo)致處理器把控制轉(zhuǎn)移到指定任務(wù)中。見圖4-37所示。






當(dāng)切換到一個(gè)新任務(wù)時(shí),處理器會(huì)執(zhí)行一下操作:

⑴ 從作為JMP或CALL指令運(yùn)算元中,或者從任務(wù)門中,或者從當(dāng)前TSS的前一任務(wù)鏈結(jié)欄位(對(duì)於由IRET引起的任務(wù)切換)中取得新任務(wù)的TSS段選擇符號(hào)。

⑵ 檢查當(dāng)前任務(wù)是否允許切換到新任務(wù)。把資料存取特權(quán)級(jí)規(guī)則應(yīng)用到JMP和CALL指令上。當(dāng)前任務(wù)的CPL和新任務(wù)段選擇符號(hào)的RPL必須小於或等於TSS段描述符號(hào)的DPL,或者引用的是一個(gè)任務(wù)門。無論目標(biāo)任務(wù)門或TSS段描述符號(hào)的DPL是何值,異常、中斷(除了使用INT n指令產(chǎn)生的中斷)和IRET一指令都允許執(zhí)行任務(wù)切換。對(duì)於INT n指令產(chǎn)生的中斷將檢查DPL。

⑶ 檢查新任務(wù)的TSS描述符號(hào)是標(biāo)注為存在的(P=1),並且TSS段長(zhǎng)度有效(大於0x67)。當(dāng)試圖執(zhí)行會(huì)產(chǎn)生錯(cuò)誤的指令時(shí),都會(huì)恢復(fù)對(duì)處理器狀態(tài)的任何改變。這使得異常處理行程的返回位址指向出錯(cuò)指令,而非出錯(cuò)指令隨后的一條指令。因此異常處理行程可以處理出錯(cuò)條件並且重新執(zhí)行任務(wù)。異常處理行程的介入處理對(duì)應(yīng)用程式來說是完全透明的。



⑷ 如果任務(wù)切換產(chǎn)生自JMP或1RET指令,處理器就會(huì)把當(dāng)前任務(wù)(老任務(wù))TSS描述符號(hào)中的忙標(biāo)志B重定;如果任務(wù)切換是由CALL指令、異?;蛑袛喈a(chǎn)生,則忙標(biāo)志B不動(dòng)。

⑸ 如果任務(wù)切換由IRET產(chǎn)生,則處理器會(huì)把臨時(shí)保存的EFLAGS映射中的NT標(biāo)志重定;如果任務(wù)切換由CALL、JMP指令或者異?;蛑袛喈a(chǎn)生,則不用改動(dòng)上述NT標(biāo)志。


⑹ 把當(dāng)前任務(wù)的狀態(tài)保存到當(dāng)前任務(wù)的TSS中。處理器會(huì)從任務(wù)寄存器中取得當(dāng)前任務(wù)TSS的基底位址,並且把一下寄存器內(nèi)容復(fù)制到當(dāng)前TSS中:所有通用寄存器、段寄存器中的段選擇符號(hào)、標(biāo)志寄存器EFLAGS以及指令指標(biāo)EIP。

⑺ 如果任務(wù)切換是由CALL指令、異?;蛑袛喈a(chǎn)生,則處理器就會(huì)把從新任務(wù)中載入的EFLAGS中的NT標(biāo)志置位元。如果任務(wù)切換產(chǎn)生自JMP或IRET指令,就不改動(dòng)新載入EFLAGS中的標(biāo)志。

⑻ 如果任務(wù)切換由CALL、JMP指令或者異?;蛑袛喈a(chǎn)生,處理器就會(huì)設(shè)置新任務(wù)TSS描述符號(hào)中的忙標(biāo)志B。如果任務(wù)切換由IRET產(chǎn)生,則不去改動(dòng)B標(biāo)志。

⑼ 使用新任務(wù)TSS的段選擇符號(hào)和描述符號(hào)載入任務(wù)寄存器TR(包括隱藏部分)。設(shè)置CR0寄存器的TS標(biāo)志。

⑽ 把新任務(wù)的TSS狀態(tài)載入進(jìn)處理器。這包括LDTR寄存器、PDBR(CR3)寄存器、
EFLAGS寄存器、EIP寄存器以及通用寄存器和段選擇符號(hào)。在此期間檢測(cè)到的任何錯(cuò)誤都將出現(xiàn)在新任務(wù)的上下文中。

⑾ 開始執(zhí)行新任務(wù)(對(duì)於異常處理行程,新任務(wù)的第一條指令顯現(xiàn)出還沒有執(zhí)行)。


當(dāng)成功地進(jìn)行了任務(wù)切換操作,當(dāng)前執(zhí)行任務(wù)的狀態(tài)總是會(huì)被保存起來。當(dāng)任務(wù)恢復(fù)執(zhí)行時(shí),任務(wù)將從保存的EIP指向的指令處開始執(zhí)行,並且所有寄存器都恢復(fù)到任務(wù)掛起時(shí)的值。

當(dāng)執(zhí)行任務(wù)切換時(shí),新任務(wù)的特權(quán)級(jí)與原任務(wù)的特權(quán)級(jí)沒有任何關(guān)系。新著任務(wù)在CS寄存器的CPL欄位指定的特權(quán)級(jí)上開始執(zhí)行。因?yàn)楦鱾€(gè)任務(wù)透過它們獨(dú)立的位址空間和TSS段相互隔絕,並且特權(quán)級(jí)規(guī)則已經(jīng)控制對(duì)TSS的存取,所以在任務(wù)切換時(shí)軟件不需要再進(jìn)行特權(quán)級(jí)檢查。

每次任務(wù)切換都會(huì)設(shè)置控制寄存器CR0中的任務(wù)切換標(biāo)志TS。該標(biāo)志對(duì)系統(tǒng)軟件非常有用。系統(tǒng)軟體可用TS標(biāo)志來協(xié)調(diào)處理器和浮點(diǎn)輔助運(yùn)算器之間的操作。TS標(biāo)志指明輔助運(yùn)算器中的上下文可能與當(dāng)前任務(wù)的不同。


4.7.5 任務(wù)鏈

TSS的前一任務(wù)連接(Backlink)欄位以及EFLAGS中的NT標(biāo)志用於返回到前一個(gè)任務(wù)操作中。NT標(biāo)志指出了當(dāng)前執(zhí)行的任務(wù)是否是巢狀嵌在另一個(gè)任務(wù)中執(zhí)行,并且當(dāng)前任務(wù)的前一任務(wù)連接欄位中存放著巢狀層中更高層任務(wù)的TSS選擇符號(hào),若有的話(見圖4-38所示)。







當(dāng)CALL指令、中斷或異常造成任務(wù)切換,處理器把當(dāng)前TSS段的選擇符號(hào)復(fù)制到新任務(wù)TSS段的前一任務(wù)鏈結(jié)欄位中,然后在EFLAGS中設(shè)置NT標(biāo)志。NT標(biāo)志指明TSS的前一任務(wù)鏈結(jié)欄位中存放有保存的TSS段選擇符號(hào)。如果軟件使用IRET指令掛起新任務(wù),處理器就會(huì)使用前一任務(wù)鏈接欄位中值和NT標(biāo)志返回到前一任務(wù)。也即如果NT標(biāo)志是置位元的話,處理器會(huì)切換到前一任務(wù)鏈接欄位的任務(wù)去執(zhí)行。


注意,當(dāng)任務(wù)切換是由JMP指令造成,那麼新任務(wù)就不會(huì)是巢狀的。也即,NT標(biāo)志會(huì)被設(shè)置為0,並且不使用前一任務(wù)鏈結(jié)欄位。JMP指令用於不希望出現(xiàn)巢狀的任務(wù)切換中。

表4-9總結(jié)了任務(wù)切換期間,忙標(biāo)志B(在TSS段描述符號(hào)中)、NT標(biāo)志、前一任務(wù)鏈結(jié)欄位和TS標(biāo)志(在CR0中)的用法。注意,執(zhí)行於任何特權(quán)級(jí)上的程式都可以修改NT標(biāo)志,因此任何程式都可以設(shè)置NT標(biāo)志並執(zhí)行IRET指令。這種做法會(huì)讓處理器去執(zhí)行當(dāng)前任務(wù)TSS的前一任務(wù)鏈結(jié)欄位指定的任務(wù)。為了避免這種偽造的任務(wù)切換執(zhí)行成功,作業(yè)系統(tǒng)應(yīng)該把每個(gè)TSS的該欄位初始化為0。






4.7.6 任務(wù)位址空間的

任務(wù)的位址空間由任務(wù)能夠存取的段構(gòu)成。這些段包括代碼段、資料段、堆棧段、TSS中引用的系統(tǒng)段以及任務(wù)代碼能夠存取的任何其他段。這些段都被映射到處理器的線性位址空間中,並且隨后被直接地或者透過分頁機(jī)制映射到處理器的實(shí)體位址空間中。

TSS中的LDT欄位可以用於給出每個(gè)任務(wù)自己的LDT 。對(duì)於一個(gè)給定的任務(wù),透過把與任務(wù)相關(guān)的所有段描述符號(hào)放入LDT中,任務(wù)的位址空間就可以與其他任務(wù)的隔絕開來。

當(dāng)然,幾個(gè)任務(wù)也可以使用同一個(gè)LDT。這是一種簡(jiǎn)單而有效的允許某些任務(wù)互相通信或控制的方法,而無須拋棄整個(gè)系統(tǒng)的保護(hù)屏障。


因?yàn)樗腥蝿?wù)都可以存取GDT,所以也同樣可以建立透過此表存取的共用段。

如果開啟了分頁機(jī)制,則TSS中的CR3暫存器欄位可以讓每個(gè)任務(wù)有它自己的頁表?;蛘?,幾個(gè)任務(wù)能夠共用相同頁表集。


把任務(wù)映射到線性和實(shí)體位址空間

有兩種方法可以把任務(wù)映射到線性位址空間和實(shí)體位址空間:

▓ 所有任務(wù)共用一個(gè)線性到實(shí)體位址空間的映射。當(dāng)沒有開啟分頁機(jī)制時(shí),就只能使用這個(gè)辦法。不開啟分頁時(shí),所有線性位址映射到相同的實(shí)體位址上。當(dāng)開啟了分頁機(jī)制,那麼透過讓所有任務(wù)使用一個(gè)頁目錄,我們就可以使用這種從線性到實(shí)體位址空間的映射形式。如果支援需求頁虛擬儲(chǔ)存技術(shù),則線性位址空間可以超過現(xiàn)有實(shí)體位址空間的大小。

▓ 每個(gè)任務(wù)有自己的線性位址空間,並映射到實(shí)體位址空間。透過讓每個(gè)任務(wù)使用不同的頁目錄,我們就可以使用這種映射形式。因?yàn)槊看稳蝿?wù)切換都會(huì)載入PDBR (控制暫存器CR3) ,所以每個(gè)任務(wù)可以有不同的頁目錄。

不同任務(wù)的線性位址空間可以映射到完全不同的實(shí)體位址上。如果不同頁目錄的條目(表項(xiàng))指向不同的頁表,而且頁表也指向?qū)嶓w位址中不同的頁面上,那麼各個(gè)任務(wù)就不會(huì)任何實(shí)體位址。

對(duì)於映射任務(wù)線性位址空間的這兩種方法,所有任務(wù)的TSS都必須存放在共用的實(shí)體位址空間區(qū)域中,並且所有任務(wù)都能存取這個(gè)區(qū)域。為了讓處理器執(zhí)行任務(wù)切換而讀取或更新TSS時(shí),TSS位址的映射不會(huì)改變,就需要使用這種映射方式。GDT所映射的線性位址空間也應(yīng)該映射到共用的實(shí)體位址空間中。否則就喪失了GDT的作用。


任務(wù)邏輯位址空間

為了在任務(wù)之間共用資料,可使用下列方法之一來為資料段建立共用的邏輯到實(shí)體位址空間的映射:

透過使用GDT中的段描述符號(hào)。所有任務(wù)必須能夠存取GDT中的段描述符號(hào)。如果GDT中的某些段描述符號(hào)指向線性位址空間中的一些段,並且這些段被映射到所有任務(wù)共用的實(shí)體位址空間中,那麼所有任務(wù)部可以共用這些段中的代碼和資料。

透過共用的LDT。兩個(gè)或多個(gè)任務(wù)可以使用相同的LDT,如果它們TSS中LDT欄位指向同一個(gè)LDT。如果一個(gè)共用的LDT中某些段描述符號(hào)指向映射到實(shí)體位址空間公共區(qū)域的段,那麼共用LDT的所有任務(wù)可以共用這些段中的所有代碼和資料。這種共用方式要比透過GDT來共用好,因?yàn)檫@樣做可以把共用局限於指定的一些任務(wù)中。系統(tǒng)中有不同LDT的其他任務(wù)沒有存取這些共用段的權(quán)利。

透過映射到線性位址空間公共位址區(qū)域的不同LDT中的段描述符號(hào)。如果線性位址空間中的這個(gè)公共區(qū)域?qū)γ總€(gè)任務(wù)都映射到實(shí)體位址空間的相同區(qū)域,那麼這些段描述符號(hào)就允許任務(wù)共用這些段。這樣的段描述符號(hào)通常稱為別名段。這個(gè)共用方式要此上面給出的方式來得更好,因?yàn)長(zhǎng)DT中的其他段描述符號(hào)可以指向獨(dú)立的未共用線性位址區(qū)域。


4.8 保護(hù)模式程式初始化

我們知道,80X86可以工作在幾種模式下。當(dāng)機(jī)器上電或硬體重定時(shí),處理器工作在8086處理器相容的真實(shí)位址模式下,並且從實(shí)體位址0xFFFFFFF0處開始執(zhí)行軟體初始化代碼(通常在EPROM中)。軟體初始化代碼首先必須設(shè)置基本系統(tǒng)功能操作必要的資料結(jié)構(gòu)資訊,例如處理中斷和異常的實(shí)模式IDT表(即中斷向量表)。如果處理器將仍然工作在實(shí)模式下,軟體必須載入作業(yè)系統(tǒng)模組和相應(yīng)資料以允許應(yīng)用程式能在實(shí)模式下可靠地執(zhí)行。如果處理器將要工作在保護(hù)模式下,那麼作業(yè)系統(tǒng)軟體就必須載入保護(hù)模式操作必要的資料結(jié)構(gòu)資訊
,然后切換到保護(hù)模式。


4.8.1 進(jìn)入保護(hù)模式時(shí)的初始化操作

保護(hù)模式所需要的一些資料結(jié)構(gòu)由處理器記憶體管理功能確定。處理器支援分段模型,可以使用從單個(gè)、統(tǒng)一的位址空間平坦模型到每個(gè)任務(wù)都具有幾個(gè)受保護(hù)位址空間的高度結(jié)構(gòu)化的多段模型。分頁機(jī)制能夠用來部分在記憶體、部分在磁片上的大型資料結(jié)構(gòu)資訊。這兩種位址轉(zhuǎn)換形式都需要作業(yè)系統(tǒng)在記憶體中為記憶體管理硬體設(shè)置所要求的資料結(jié)構(gòu)。因此在處理器能夠被切換到保護(hù)模式下執(zhí)行之前,作業(yè)系統(tǒng)載入和初始化軟(bootsect.s、setup.s和head.s)必須在記憶體中先設(shè)置好保護(hù)模式下使用的資料結(jié)構(gòu)的基本資訊。這些資料結(jié)構(gòu)包括
以下幾種:

▓ 保護(hù)模式中斷描述符號(hào)表IDT;

▓ 全域描述符號(hào)表GDT;

▓ 任務(wù)狀態(tài)段TSS;

▓ 區(qū)域描述符號(hào)表LDT;

▓ 若使用分頁機(jī)制,則起碼需要設(shè)置一個(gè)頁目錄和一個(gè)頁表;

▓ 處理器切換到保護(hù)模式下執(zhí)行的代碼段;

▓ 含有中斷和異常處理程式的代碼模組。

在能夠切換到保護(hù)模式之前,軟體初始化代碼還必須設(shè)置以下系統(tǒng)寄存器:

▓ 全域描述符號(hào)表基底位址寄存器GDTR;

▓ 中斷描述符號(hào)表基底位址寄存器IDTR;

▓ 控制寄存器CRl- -CR3;

在初始化了這些資料結(jié)構(gòu)、代碼模組和系統(tǒng)暫存器之后,透過設(shè)置CR0寄存器的保護(hù)模式標(biāo)志PE(位元0) ,處理器就可以切換到保護(hù)模式下執(zhí)行。


保護(hù)模式系統(tǒng)結(jié)構(gòu)表

軟體初始化期間在記憶體中設(shè)置的保護(hù)模式系統(tǒng)表主要依賴於作業(yè)系統(tǒng)將要支援的記憶體管理類型:平坦的、平坦並支援分頁的、分段的或者分段並支援分頁的。

為了實(shí)現(xiàn)無分頁的平坦記憶體模型,軟體初始化代碼必須起碼設(shè)置具有一個(gè)代碼段和一個(gè)資料段的GDT表。當(dāng)然GDT表第l項(xiàng)還需要放置一個(gè)空描述符號(hào)。堆棧可以放置在普通可讀寫資料段中,因此並不需要專門的堆棧描述符號(hào)。支援分頁機(jī)制的平坦記憶體模型還需要一個(gè)頁目錄和至少一個(gè)頁表。在可以使用GDT表之前,必須使用LGDT指令把GDT表的基底位址和長(zhǎng)度值載入到GDTR寄存器中。

而多段模型則還需要用於作業(yè)系統(tǒng)的其他段,以及用於每個(gè)應(yīng)用程式的段和LDT表段。LDT表的段描述符號(hào)要求存放在GDT表中。某些作業(yè)系統(tǒng)會(huì)為應(yīng)用程式另行分配新段和新的LDT段。這種做法為動(dòng)態(tài)程式設(shè)計(jì)環(huán)境提供了最大靈活性,例如Linux作業(yè)系統(tǒng)就使用了這種方式。像程序控制器那樣的嵌入式系統(tǒng)可以預(yù)先為固定數(shù)量的應(yīng)用程式分配固定數(shù)量的段和LDT,這是實(shí)現(xiàn)即時(shí)系統(tǒng)軟件環(huán)境結(jié)構(gòu)的一種簡(jiǎn)單而有效的方法。


保護(hù)模式異常和中斷初始化

軟體初始化代碼必須設(shè)置一個(gè)保護(hù)模式IDT,其中最少需要含有處理器可能產(chǎn)生的每個(gè)異常向量對(duì)應(yīng)的門描述符號(hào)。如果使用了中斷或陷阱門,那麼門描述符號(hào)可以都指向包含中斷和異常處理行程的同一個(gè)代碼段。若使用了任務(wù)門,那麼每個(gè)使用任務(wù)門的異常處理行程都需要一個(gè)TSS以及相關(guān)的代碼、資料和堆棧段。如果允許硬件產(chǎn)生中斷,那麼必須在IDT中為一個(gè)或多個(gè)中斷處理行程設(shè)置門描述符號(hào)。

在可以使用IDT之前,必須使用LIDT指令把IDT表基底位址和長(zhǎng)度載入到IDTR寄存器中。


分頁機(jī)制初始化

分頁機(jī)制由控制寄存器CR0中的PG標(biāo)志設(shè)置。當(dāng)這個(gè)標(biāo)志被清除0時(shí)(即硬體重定時(shí)的狀態(tài)),分頁機(jī)制被關(guān)閉:當(dāng)設(shè)置了PG標(biāo)志,就開啟分頁機(jī)制。在設(shè)置PG標(biāo)志之前,必須先初始化以下資料結(jié)構(gòu)和寄存器:

▓ 軟體必須在實(shí)體記憶體中建立至少一個(gè)頁目錄和一個(gè)頁表。如果頁目錄表中含有指向自身的目錄項(xiàng)時(shí),可以不使用頁表。此時(shí)頁目錄表和頁表被存放在同一頁面中。

▓ 把頁目錄表的物理基底位址載入到CR3寄存器中(也稱為PDBR寄存器)。

▓ 處理器處?kù)侗Wo(hù)模式下。如果滿足所有其他限制,則PG和PE標(biāo)志可以同時(shí)設(shè)置。
為保持相容性,設(shè)置PG標(biāo)志(以及PE標(biāo)志)時(shí)必須遵守以下規(guī)則:

▓ 設(shè)置PG標(biāo)志的指令應(yīng)該立刻跟隨一條JMP指令。MOV CR0指令后面的JMP指令會(huì)改變執(zhí)行流,所以它會(huì)清空80X86處理器已經(jīng)取得或已解碼的指令。然而,Pentium及以上處理器使用了分支目標(biāo)緩沖器BTB(Branch Target Buffer)為分支代碼定向,因此減去了為分支指令更新佇列的需要。
▓ 設(shè)置PG標(biāo)志到跳轉(zhuǎn)指令JMP之間的代碼必須來自對(duì)等映射(即跳轉(zhuǎn)之前的線性位址與開啟分頁后的實(shí)體位址相同)的一個(gè)頁面上。


多工初始化

如果將要使用多工機(jī)制,並且/或者允許改變特權(quán)級(jí),那麼軟件初始化代碼必須至少設(shè)置一個(gè)TSS及相應(yīng)的TSS段描述符號(hào)(因?yàn)樘貦?quán)級(jí)0、l和2的各堆棧段指標(biāo)需要從TSS中取得) 。在建立TSS描述符號(hào)時(shí)不要將其標(biāo)注為忙(不要設(shè)置忙標(biāo)志) ,該標(biāo)志僅由處理器在執(zhí)行任務(wù)切換時(shí)設(shè)置。與LDT段描述符號(hào)相同,TSS的描述符號(hào)也存放在GDT中。

在處理器切換到保護(hù)模式之后,可以用LTR指令把TSS段描述符號(hào)的選擇符號(hào)載入到任務(wù)暫存器TR中。這個(gè)指令會(huì)把TSS標(biāo)記成忙狀態(tài)(B=1) ,但是並不執(zhí)行任務(wù)切換操作。然后處理器可以使用這個(gè)TSS來定位特權(quán)級(jí)0、1和2的堆棧。在保護(hù)模式中,軟件進(jìn)行第一次任務(wù)切換之前必須首先載入TSS段的選擇符號(hào),因?yàn)槿蝿?wù)切換會(huì)把當(dāng)前任務(wù)狀態(tài)復(fù)制到該TSS中。

在LTR指令執(zhí)行之后,隨后對(duì)任務(wù)暫存器的操作由任務(wù)切換進(jìn)行。與其他的段和LDT類似,TSS段和TSS段描述符號(hào)可以預(yù)先設(shè)置好,也可以在需要時(shí)才進(jìn)行設(shè)置。


4.8.2模式切換

為了讓處理器工作在保護(hù)模式下,必須從真實(shí)位址模式下進(jìn)行模式切換操作。一旦進(jìn)入保護(hù)模式,軟件通常不會(huì)再需要回到真實(shí)位址模式。為了還能執(zhí)行為真實(shí)位址模式編制的程式,通常在虛擬-8086模式中執(zhí)行比再切換回實(shí)模式下執(zhí)行更為方便。


切換到保護(hù)模式

在切換到保護(hù)模式之前,必須首先載入一些起碼的系統(tǒng)資料結(jié)構(gòu)和代碼模組。一旦建立了這些系統(tǒng)表,軟件初始化代碼就可以切換到保護(hù)模式中。透過執(zhí)行在CR0寄存器中設(shè)置PE標(biāo)志的MOV CR0指令,我們就可以進(jìn)行保護(hù)模式。(在同一個(gè)指令中,CR0的PG標(biāo)志可用于開啟分頁機(jī)制。)剛進(jìn)入保護(hù)模式中執(zhí)行時(shí),特權(quán)級(jí)是0。為了保證程式的相容性,切換操作應(yīng)該按照以下步驟進(jìn)行:

⑴ 禁止中斷。使用CLI指令可以禁止可遮罩硬體中斷。NMI會(huì)由硬體電路來禁止。同時(shí)軟體應(yīng)該確保在模式切換操作期間不產(chǎn)生異常和中斷。

⑵ 執(zhí)行LGDT指令把GDT表的基底位址載入進(jìn)GDTR暫存器。

⑶ 執(zhí)行在控制暫存器CR0中設(shè)置PE標(biāo)志(可選同時(shí)設(shè)置PG標(biāo)志)的MOV CR0指令。

⑷ 在MOV CR0指令之后立刻執(zhí)行一個(gè)遠(yuǎn)跳轉(zhuǎn)JMP或遠(yuǎn)呼叫CALL指令。這個(gè)操作通常是遠(yuǎn)跳轉(zhuǎn)到或遠(yuǎn)呼叫指令流中的下一條指令。

⑸ 若要使用區(qū)域描述符號(hào)表,則執(zhí)行LLDT指令把LDT段的選擇符號(hào)載入到LDTR寄存器中。
⑹ 執(zhí)行LTR指令,用初始保護(hù)模式任務(wù)的段選擇符號(hào)或者可寫記憶體區(qū)域的段描述符號(hào)載入任務(wù)寄存器TR。這個(gè)可寫記憶體區(qū)域用於在任務(wù)切換時(shí)存放任務(wù)的TSS資訊。

⑺ 在進(jìn)入保護(hù)模式后,段寄存器仍然含有在真實(shí)位址模式時(shí)的內(nèi)容。第4步中的JMP或CALL指令會(huì)重置CS寄存器。執(zhí)行以下操作之一可以更新其余段寄存器的內(nèi)容:其余段寄存器的內(nèi)容可透過重新載入或切換到一個(gè)新任務(wù)來更新。

⑻ 執(zhí)行LIDT指令把保護(hù)模式IDT表的基底位址和長(zhǎng)度載入到IDTR寄存器中。

⑼ 執(zhí)行STI指令開啟可遮罩硬體中斷,並且執(zhí)行必要的硬體操作開啟NMI中斷。


另外,MOV CR0指令之后緊接著的JMP或CALL指令會(huì)改變執(zhí)行流。如果開啟了分頁機(jī)制,那麼MOV CR0指令到JMP或CALL指令之間的代碼必須來自對(duì)等映射(即跳轉(zhuǎn)之前的線性位址與開敔分頁后的實(shí)體位址相同)的一個(gè)頁面上。而JMP或CALL指令跳轉(zhuǎn)到的目標(biāo)指令並不需要處?kù)秾?duì)等映射頁面上。


切換回真實(shí)位址模式

若想切換回真實(shí)位址模式,則可以使用MOV CR0指令把控制暫存器CR0中的PE標(biāo)志清0。重新進(jìn)入真實(shí)位址模式的行程應(yīng)該按照以下步驟進(jìn)行:

⑴ 禁止中斷。使用CLI指令可以禁止可遮罩硬件中斷。NMI會(huì)由硬件電路來禁止。同時(shí)
軟體應(yīng)該確保在模式切換操作期間不產(chǎn)生異常和中斷。

⑵ 如果已開啟分頁機(jī)制,那麼需要執(zhí)行:

● 把程式的控制轉(zhuǎn)移到對(duì)等映射的線性位址處(即線性位址等於實(shí)體位址)’

● 確保GDT和IDT在對(duì)等映射的頁面上。

● 清除CR0中的PG標(biāo)志。

● CR3寄存器中設(shè)置為0x00,用於更新TLB緩沖。

⑶ 把程式的控制轉(zhuǎn)移到長(zhǎng)度為64KB(0xFFFF)的可讀段中。這步操作使用實(shí)模式要求的段長(zhǎng)度載入CS寄存器。

⑷ 使用指向含有以下設(shè)置值的描述符號(hào)的選擇符號(hào)來載入SS、DS、ES、FS和GS段寄存器。

● 段限長(zhǎng)Limit:64KB

● 位元組顆粒度(G=0)。

● 向上擴(kuò)展(E=0)。

● 可寫(W=1)。

● 存在(P=I)。

⑸ 執(zhí)行LIDT指令來指向在1 MB實(shí)模式位址范圍內(nèi)的真實(shí)位址模式中斷表。

⑹ 清除CR0中的PE標(biāo)志來切換到真實(shí)位址模式。

⑺ 執(zhí)行一個(gè)遠(yuǎn)跳轉(zhuǎn)指令跳轉(zhuǎn)到一個(gè)實(shí)模式程式中。這步操作會(huì)更新指令佇列並且為CS寄存器載入合適的基底位址和存取許可權(quán)值。

⑻ 載入真實(shí)位址模式程式碼會(huì)使用的SS、DS、ES、FS和GS寄存器。

⑼ 執(zhí)行STI指令開啟可遮罩硬件中斷,並且執(zhí)行必要的硬件操作開啟NMI中斷。


4.9 一個(gè)簡(jiǎn)單的多工內(nèi)核實(shí)例

作為對(duì)本章和前幾章內(nèi)容的總結(jié),本節(jié)完整描述了一個(gè)簡(jiǎn)單多工內(nèi)核的設(shè)計(jì)和實(shí)現(xiàn)方法。這個(gè)內(nèi)核示例中包含兩個(gè)特權(quán)級(jí)3的用戶任務(wù)和一個(gè)系統(tǒng)呼叫中斷行程。我們首先說明這個(gè)簡(jiǎn)單內(nèi)核的基本結(jié)構(gòu)和載入執(zhí)行的基本原理,然后描述它是如何被載入進(jìn)機(jī)器RAM記憶體中以及兩個(gè)任務(wù)是如何進(jìn)行切換執(zhí)行的。最后我們給出實(shí)現(xiàn)這個(gè)簡(jiǎn)單內(nèi)核的來源程式:開機(jī)啟動(dòng)引導(dǎo)程式boot.s和保護(hù)模式多工內(nèi)核程式head.s。


4.9.1 多工程式結(jié)構(gòu)和工作原理

本節(jié)給出的內(nèi)核實(shí)例由2個(gè)檔構(gòu)成。一個(gè)是使用as86語言編制的開機(jī)啟動(dòng)程式boot.s,用於在電腦系統(tǒng)加電時(shí)從啟動(dòng)碟上把內(nèi)核代碼載入到記憶體中;另一個(gè)是使用GNU as組合語言編制的內(nèi)核程式head.s,其中實(shí)現(xiàn)了兩個(gè)執(zhí)行在特權(quán)級(jí)3上的任務(wù)在時(shí)鐘中斷控制下相互切換執(zhí)行,並且還實(shí)現(xiàn)了在螢?zāi)簧巷@示字元的一個(gè)系統(tǒng)呼叫。我們把這兩個(gè)任務(wù)分別稱為任務(wù)A和任務(wù)B(或任務(wù)0和任務(wù)1),它們會(huì)呼叫這個(gè)顯示系統(tǒng)呼叫在螢?zāi)簧戏謩e顯示出字元‘A’和‘B’直到每個(gè)10毫秒切換到另一個(gè)任務(wù)。任務(wù)A連續(xù)回圈地呼叫系統(tǒng)呼叫在螢?zāi)簧巷@示字元‘A’;任務(wù)B則一直顯示字元‘B’。若要終止這個(gè)內(nèi)核實(shí)例程式,則需要重新啟動(dòng)機(jī)器,或者關(guān)閉執(zhí)行的模擬PC執(zhí)行環(huán)境軟件。

Boot.s程式編譯出的代碼共512位元組,將被存放在軟碟映射檔的第一個(gè)磁區(qū)中,見圖4-39所示。 PC機(jī)在加電啟動(dòng)時(shí),ROM BIOS中的程式會(huì)把啟動(dòng)盤上第一個(gè)磁區(qū)載入到實(shí)體記憶體0x7c00(31KB)位置開始處,並把執(zhí)行權(quán)轉(zhuǎn)移到0x7c00處開始執(zhí)行boot程式碼。






boot程式的主要功能是把軟碟或映射檔中的head內(nèi)核代碼載入到記憶體某個(gè)指定位置處,並在設(shè)置好臨時(shí)GDT表等資訊后,把處理器設(shè)置成執(zhí)行在保護(hù)模式下,然后跳轉(zhuǎn)到head代碼處去執(zhí)行內(nèi)核代碼。實(shí)際上,boot.s程式會(huì)首先利用ROM BIOS中斷int 0x13把軟碟中的head代碼讀入到記憶體0x10000(64KB)位置開始處,然后再把這段head代碼移動(dòng)到記憶體0開始處。最后設(shè)置控制寄存器CR0中的開啟保護(hù)執(zhí)行模式標(biāo)志,並跳轉(zhuǎn)到記憶體0處開始執(zhí)行head代碼。Boot程式碼在記憶體中移動(dòng)head代碼的示意圖見圖4-40所示。





把head內(nèi)核代碼移動(dòng)到實(shí)體記憶體0開始處的主要原因是為了設(shè)置GDT表時(shí)可以簡(jiǎn)單一些,因而也能讓head.s程式盡量短一些。但是我們不能讓boot程式把head代碼從軟碟或映射檔中直接載入到記憶體0處。因?yàn)檩d入操作需要使用ROM BIOS提供的中斷行程,而BIOS使用的中斷向量表正處?kù)队洃涹w0開始的地方,並且在記憶體l Kb開始處是BIOS程式使用的資料區(qū),所以若直接把head代碼載入到記憶體0處將使得BIOS中斷行程不能正常執(zhí)行。當(dāng)然我們也可以把head代碼載入到記憶體0xl0000處后就直接跳轉(zhuǎn)到該處執(zhí)行head代碼,使用這種方式的來源程式可從oldlinux.org網(wǎng)站下載,見下面說明。

Head.s程式執(zhí)行在32位元保護(hù)模式下,其中主要包括初始設(shè)置的代碼、時(shí)鐘中斷int 0x08的行程代碼、系統(tǒng)呼叫中斷int 0x80的行程代碼以及任務(wù)A和任務(wù)B等的代碼和資料。其中初始設(shè)置工作主要包括:①重新設(shè)置GDT表:②設(shè)置系統(tǒng)計(jì)時(shí)器晶片;③重新設(shè)置IDT表並且設(shè)置時(shí)鐘和系統(tǒng)呼叫中斷門:④移動(dòng)到任務(wù)A中執(zhí)行。

在虛擬位址空間中head.s程式的內(nèi)核代碼和任務(wù)代碼分配圖如圖4-4l所示。實(shí)際上,本內(nèi)核示例中所有代碼和資料段部對(duì)應(yīng)到實(shí)體記憶體同一個(gè)區(qū)域上,即從實(shí)體記憶體0開始的區(qū)域。GDT中全域代碼段和資料段描述符號(hào)的內(nèi)容都設(shè)置為:基底位址為0x0000;段限長(zhǎng)值為0x07ff 。因?yàn)轭w粒度為1,所以實(shí)際段長(zhǎng)度為8MB。而全域顯示資料段被設(shè)置成:基底位址為0xb8000:段限長(zhǎng)值為0x0002,所以實(shí)際段長(zhǎng)度為8KB,對(duì)應(yīng)到顯示記憶體區(qū)域上。







兩個(gè)任務(wù)的在LDT中代碼段和資料段描述符號(hào)的內(nèi)容也都設(shè)置為:基底位址為0x0000;段限長(zhǎng)值為0x03ff,實(shí)際段長(zhǎng)度為4MB 。因此在線性位址空間中這個(gè)“內(nèi)核”的代碼和資料段與任務(wù)的代碼和資料段都從線性位址。開始並且由於沒有採(cǎi)用分頁機(jī)制,所以它們都直接對(duì)應(yīng)實(shí)體位址0開始處。在head程式編譯出的目標(biāo)檔中以及最終得到的軟碟映射檔中,代碼和資料的組織形式見圖4-42所示。






由於處?kù)短貦?quán)級(jí)0的代碼不能直接把控制權(quán)轉(zhuǎn)移到特權(quán)級(jí)3的代碼中執(zhí)行,但中斷返回操作是可以的,因此當(dāng)初始化GDT、IDT和定時(shí)晶片結(jié)束后,我們就利用中斷返回指令I(lǐng)RET來啟動(dòng)執(zhí)行第l個(gè)任務(wù)。具體實(shí)現(xiàn)方法是在初始堆棧init_stack中人工設(shè)置一個(gè)返回環(huán)境。即把任務(wù)。的TSS段選擇符號(hào)載入到任務(wù)寄存器LTR中、LDT段選擇符號(hào)載入到LDTR中以后,把任務(wù)。的用戶堆棧指標(biāo)(0x17:init_stack)和代碼指標(biāo)(0x0f:task0)以及標(biāo)志寄存器值壓入堆棧。然后執(zhí)行中斷返回指令I(lǐng)RET。該指令會(huì)彈出堆棧上的堆棧指標(biāo)作為任務(wù)。用戶堆棧指標(biāo),恢復(fù)假設(shè)的任務(wù)0的標(biāo)志寄存器內(nèi)容,並且彈出堆棧中代碼指標(biāo)放入CS:EIP寄存器中,從而開始執(zhí)行任務(wù)0的代碼,完成了從特權(quán)級(jí)0到特權(quán)級(jí)3代碼的控制轉(zhuǎn)移。

為了每隔10毫秒切換執(zhí)行的任務(wù),head.s程式中把計(jì)時(shí)器晶片8253的通道0設(shè)置成每經(jīng)過l0毫秒就向中斷控制晶片8259A發(fā)送一個(gè)時(shí)鐘中斷請(qǐng)求信號(hào)。PC機(jī)的ROM BIOS開機(jī)時(shí)已經(jīng)在8259A中把時(shí)鐘中斷請(qǐng)求信號(hào)設(shè)置成中斷向量8,因此我們需要在中斷8的處理行程中執(zhí)行任務(wù)切換操作。任務(wù)切換的實(shí)現(xiàn)方法是查看current變數(shù)中當(dāng)前執(zhí)行任務(wù)號(hào)。如果current當(dāng)前是0,就利用任務(wù)l的TSS選擇符號(hào)作為運(yùn)算元執(zhí)行遠(yuǎn)跳轉(zhuǎn)指令,從而切換到任務(wù)l中執(zhí)行,否則反之。

每個(gè)任務(wù)在執(zhí)行時(shí),會(huì)首先把一個(gè)字元的ASCII碼放入寄存器AL中,然后呼叫系統(tǒng)中斷呼叫int 0x80,而該系統(tǒng)呼叫處理行程則會(huì)呼叫一個(gè)簡(jiǎn)單的字元寫到螢?zāi)桓背淌剑逊e存器AL中的字元顯示在螢?zāi)簧?,同時(shí)把字元顯示的螢?zāi)坏南乱粋€(gè)位置記錄下來,作為下一次顯示字元的螢?zāi)晃恢?。在顯示過一個(gè)字元后,任務(wù)代碼會(huì)使用回圈語句延遲一段時(shí)間,然后又跳轉(zhuǎn)到任務(wù)代碼開始處繼續(xù)回圈執(zhí)行,直到執(zhí)行了l0毫秒而發(fā)生了定時(shí)中斷,從而代碼會(huì)切換到另一個(gè)任務(wù)去執(zhí)行。對(duì)於任務(wù)A,寄存器AL中將始終存放字元‘A’,而任務(wù)B執(zhí)行時(shí)AL中始終存放字元‘B’。因此在程式執(zhí)行時(shí)我們將看到一連串的字元‘A’和一連串的字元‘A’間隔地連續(xù)不斷地顯示在螢?zāi)簧?,見圖4-43所示。






圖4-43是我們?cè)贐ochs模擬軟件中執(zhí)行這個(gè)內(nèi)核示例的螢?zāi)伙@示情況。細(xì)心的讀者會(huì)發(fā)現(xiàn),在圖中底端一行上顯示出一個(gè)字元‘C’。這是由於PC機(jī)偶然產(chǎn)生了一個(gè)不是時(shí)鐘和系統(tǒng)呼叫的其他中斷而引起的。因?yàn)槲覀円呀?jīng)在程式給所有其他中斷安裝了一個(gè)預(yù)設(shè)中斷處理程式,在這個(gè)程式中會(huì)顯示一個(gè)字元‘C’,然后退出中斷。

下面列出boot.s和head.s程式的詳細(xì)注釋。有關(guān)這個(gè)簡(jiǎn)單內(nèi)核示例的編譯和執(zhí)行方法請(qǐng)參考我們最后一章中“編譯執(zhí)行簡(jiǎn)單內(nèi)核示例程式”一節(jié)內(nèi)容。


4.9.2 開機(jī)啟動(dòng)程式boot.s

為了盡量讓程式簡(jiǎn)單,這個(gè)開機(jī)啟動(dòng)磁區(qū)程式僅能夠載入長(zhǎng)度不超過16個(gè)磁區(qū)的head代碼,並且直接使用了ROM BIOS預(yù)設(shè)設(shè)置的中斷向量號(hào),即定時(shí)中斷請(qǐng)求處理的中斷號(hào)仍然是8。這與Linux系統(tǒng)中使用的不同。Linux系統(tǒng)會(huì)在內(nèi)核初始化時(shí)重新設(shè)置8259A中斷控制晶片,并把時(shí)鐘中斷請(qǐng)求信號(hào)對(duì)應(yīng)到中斷0x20以上,詳細(xì)說明請(qǐng)參考“內(nèi)核開機(jī)啟動(dòng)程式”一章的內(nèi)容。






4.9.3多工內(nèi)核程式head.s

在進(jìn)入保護(hù)模式后,head.s程式重新建立和設(shè)置IDT、GDT表的主要原因是為了讓程式在結(jié)構(gòu)上比較清晰,也為了與后面Linux 0.12內(nèi)核原始碼中這兩個(gè)表的設(shè)置方式保持一致。當(dāng)然,就本程式來說我們完全可以直接使用boot.s中設(shè)置的IDT和GDT表位置,填入適當(dāng)?shù)拿枋龇?hào)項(xiàng)即可。







192 .1ong 0,0,0,0,0 /* espl,ssl,esp2,ss2,cr3 */
193 .1ong 0,0,0,0,0 /* eip,eflags,eax,ecx,edx */
194 .1ong 0,0,0,0,0 /* ebx esp,ebp,esi,edi */
195 .1ong 0,0,0,0,0 /* es,cs,ss,ds,fs,gs */
196 .1ong LDT0_SEL,0x8000000 /* ldt,trace bitmap */
197
198 .fill 128,4,0 #這是任務(wù)0的內(nèi)核堆??臻g。
199 krn_stk0 :
200
201 #下面是任務(wù)l的LDT表段內(nèi)容和TSS段內(nèi)容。
202 .align 3
203 ldt1: .quad 0x0000000000000000 #第l個(gè)描述符號(hào),不用。
204 .quad 0x00c0fa00000003ff #選擇符號(hào)是0x0f,基底位址 = 0x00000。
205 .quad 0x00c0f200000003ff #選擇符號(hào)是0x17,基底位址 = 0x00000。
206
207 tss1: .long 0 /* back link */
208 .long krn_stk1,0x10 /* esp0,ss0 */
209 .1ong 0,0,0,0,0 /* espl,ssl,esp2,ss2,cr3 */
210 .1ong task1,0x200 /* eip,eflags */
211 .1ong 0,0,0,0,0 /* eax,ecx,edx,ebx */
212 .1ong usr_stk1,0,0,0 /* esp,ebp,esi,edi */
213 .1ong 0x17,0x0f,0x17,0x17,0x17,0x17 /* es,cs,ss,ds,fs,gs */
214 .1ong LDT1_SEL,0x8000000 /* ldt,trace bitmap */
215
216 .fill 128,4,0 #這是任務(wù)1的內(nèi)核堆??臻g其用戶堆棧直接使用初始堆??臻g。
217 krn_stk1 :
218
219 #下面是任務(wù)0和任務(wù)1的程式,它們分別回圈顯示字元‘A’和‘B’。
220 task0 :
221 movl $0x17,%eax #首先讓DS指向任務(wù)的區(qū)域資料段。
222 movw %ax, %ds #因?yàn)槿蝿?wù)沒有使用區(qū)域資料,所以這兩句可省略。
223 movl $65, %al #把需要顯示的字元‘A’放入AL寄存器中。
224 int $0x80 #執(zhí)行系統(tǒng)呼叫,顯示字元。
225 movl $0xfff,%ecx #執(zhí)行回圈,起延時(shí)作用。
226 l : loop lb
227 jmp task0 #跳轉(zhuǎn)到任務(wù)代碼開始處繼續(xù)顯示字元。
228 taskl :
229 movl $66, %al #把需要顯示的字元‘B’放入AL寄存器中。
230 int $0x80 #執(zhí)行系統(tǒng)呼叫,顯示字元。
231 movl $0xfff,%ecx #延時(shí)一段時(shí)間,並跳轉(zhuǎn)到開始處繼續(xù)回圈顯示。
232 l : loop lb
233 jmp taskl
234
235 .fill 128,4,0 #這是任務(wù)l的用戶堆??臻g
236 usr_stk1 :







To be continued......
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
TSS任務(wù)狀態(tài)段
Linux進(jìn)程-進(jìn)程的創(chuàng)建
任務(wù)門,調(diào)用門,中斷門,陷阱門
80386保護(hù)模式總結(jié)
第10章 保護(hù)模式下的存儲(chǔ)器管理
Linux 內(nèi)核
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服