x86 的 TSS 任务切换机制
轉(zhuǎn)自:http://blog.chinaunix.net/uid-587665-id-2732907.html
【0】寫在前面
segment descriptors 構(gòu)建保護(hù)模式下的最基本、最根本的執(zhí)行環(huán)境。system descriptors 則構(gòu)建保護(hù)模式下的核心組件:
1、TSS descriptor 提供硬件級(jí)的進(jìn)程切換機(jī)制
- 2、LDT descriptor 供進(jìn)程使用多個(gè) descriptor
- 3、Gate descriptor 提供 processor 權(quán)限級(jí)別的切換機(jī)制。
5.7.1、 TSS 提供的進(jìn)程切換機(jī)制
- TSS 是一段內(nèi)存區(qū)域,存放進(jìn)程相關(guān)的執(zhí)行環(huán)境信息。初始化的 TSS 是由用戶提供,進(jìn)程切換時(shí)的保存信息由 processor 執(zhí)行。
5.7.1.1、 三個(gè)元素構(gòu)成 TSS 環(huán)境:
1、 TSS descriptor:這個(gè) descriptor 屬于 system descriptor 類型,它的 S (system)位是 0。
下面列出 TSS descriptors 的類型值:
0001: 16-bit TSS 0011: busy 16-bit TSS 1001: 32-bit TSS 1011: busy 32-bit TSS以上是 x86 下的 TSS descriptor 類型,分為 16 和 32 位 TSS,x64 的 long mode 下 32 位的 TSS descriptor 將變?yōu)?64 位 TSS descriptor。
情景提示:
關(guān)于 TSS 的 busy 與 available 狀態(tài):
- 1、 TSS descriptor 的類型指明是 busy 與 available 狀態(tài)。
- 2、 TSS descriptor 的 busy 與 available 狀態(tài)由 processor 去控制。即:由 processor 置為 busy 或 avaibable。 除了初始的 TSS descriptor 外。
- TSS 的 busy 狀態(tài)主要用來支持任務(wù)的嵌套。TSS descriptor 為 busy 狀態(tài)時(shí)是不可進(jìn)入執(zhí)行的。同時(shí)防止 TSS 進(jìn)程切換機(jī)制出現(xiàn)遞歸現(xiàn)象。
2、 TSS selector 以及 TR(Task Register)寄存器
TR 寄存器的結(jié)構(gòu)與 segment registers 是完全一致的,即:由軟件可見的 selector 部分與 processor 可見的隱藏部分(信息部分)構(gòu)成。
TR.selector 與 CS.selector 中的 selector 意義是完全一樣的。其 descriptor 的加載也是一樣的。
即: TR.selector 在 GDT / LDT 中索引查找到 TSS descriptor 后,該 TSS descriptor 將被加載到 TR 寄存的隱藏部分。當(dāng)然在加載到 TR 寄存器之前,要進(jìn)行檢查通過了才可加載到 TR 寄存器。若加載 non-TSS descriptor 進(jìn)入 TR 則會(huì)產(chǎn)生 #GP 異常。
3、 TSS 塊(Task Status Segment)
像 code segment 或 data segments 一樣,最終的 TSS segment 由 TSS descriptor 來決定。 TSS descriptor 指出 TSS segment 的 base、limit 及 DPL 等信息。
TSS segment 存放 eflags 寄存器、GPRs 寄存器及相關(guān)的權(quán)限級(jí)別的 stack pointer (ss & sp)、CR3 等等信息。
5.7.1.2、 TSS 機(jī)制的建立
對(duì)于多任務(wù) OS 來說,TSS segment 是必不可少的,系統(tǒng)至少需要一個(gè) TSS segment,但是現(xiàn)在的 OS 系統(tǒng)不使用 TSS 機(jī)制來進(jìn)行任務(wù)的切換。
情景提示:
- TSS 存在的唯一理由是:需要提供 0 ~ 2 權(quán)限級(jí)別的 stack pointer,當(dāng)發(fā)生 stack 切換時(shí),必須使用 TSS 提供的相應(yīng)的 stack pointer。
- 但是:若提供空的 TSS segment,或者可以考慮以直接傳遞 stack pointer 的方式實(shí)現(xiàn) stack 切換,即便是這樣設(shè)計(jì) processor 要讀取 TSS segment 這一工作是必不可少的。
下面的指令用來建立初始的 TSS segment:
LTR word ptr [TSS_Selector] /* 在 [TSS_selector] 提供 TSS selector */
或:LTR ax /* 在 ax 寄存器里提供 TSS selector */ltr 指令使用提供的 selector 在 GDT / LDT 里索引查找到 TSS descriptor 后,加載到 TR 寄存器里。初始的 TSS descriptor 必須設(shè)為 available 狀態(tài),否則不能加載到 TR。processor 加載 TSS descriptor 后,將 TSS descriptor 置為 busy 狀態(tài)。
5.7.1.3、 TSS 進(jìn)程切換的過程
當(dāng)前進(jìn)程要切換另一個(gè)進(jìn)程時(shí),可以使用 2 種 selector 進(jìn)行:使用 TSS selector 以及 Task gate selector(任務(wù)門符)。
call 0x2b:0x00000000 /* 假設(shè) 0x2b 為 TSS selector */ call 0x3b:0x00000000 /* 假設(shè) 0x3b 為 Task-gate selector */TSS 提供的硬件級(jí)進(jìn)程切換機(jī)制較為復(fù)雜,大多數(shù) OS 不使用 TSS 機(jī)制,是因?yàn)閳?zhí)行的效能太差了。上面的兩條指令的 TSS 進(jìn)程切換的過程如下:
1、使用 TSS selector
call 0x2b:0x00000000 /* 0x2b 為 TSS selector */
這里使用 jmp 指令與 call 指令會(huì)有些差別,call 允許 TSS 進(jìn)程切換的嵌套,jmp 不允許嵌套。(1)processor 使用 TSS selector (0x2b) 在 GDT 索引查找第 5 個(gè) descriptor
(2)processor 檢查找到的 descriptor 類型是否是 TSS descriptor,不是的話將產(chǎn)生 #GP 異常。是否為 available TSS,若目標(biāo) TSS descriptor 是 busy 的話,同樣將產(chǎn)生 #GP 異常。
(3)processor 進(jìn)行另一項(xiàng)檢查工作:權(quán)限的檢查,CPL <= DPL 并且 selector.RPL <= DPL 即為通過,否則產(chǎn)生 #GP 異常。
(4)當(dāng)前進(jìn)程的執(zhí)行環(huán)境被保存在當(dāng)前進(jìn)程的 TSS segment 中。
情景提示:
在這一步里,此時(shí)還沒發(fā)生 TSS selector 切換,processor 把當(dāng)前進(jìn)程的環(huán)境信息保存在當(dāng)前的 TSS segment 中。(5)這里發(fā)生了 TSS selector 切換。新的 TSS selector 被加載到 TR.selector,而新的 TSS descriptor 也被加載到 TR 寄存的隱藏部分。
情景提示:
- (1)這里,processor 還要將舊的 TSS selector 保存在當(dāng)前的 TSS segment(新加載的 TSS)中的 link 域。這個(gè) TSS segment 中的 link 其實(shí)就是 old TSS selector 域,用來進(jìn)程返回時(shí)獲得原來的 TSS selector 。從而實(shí)現(xiàn)任務(wù)的嵌套機(jī)制。
- (2)processor 將當(dāng)前 eflags 寄存器的 NT(Nest Task)標(biāo)志位置為 1,表明當(dāng)前的進(jìn)程是嵌套內(nèi)層。
(6)processor 從當(dāng)前的 TSS segment 取出新進(jìn)程的執(zhí)行環(huán)境,包括:各個(gè) selector registers(segment registers)、GPRs、stack pointer (ss & sp)、CR3 寄存器以及 eflags 寄存器等。
在這一步,在加載 selectors 進(jìn)入 segment registers 之前,還必須經(jīng)過相關(guān)的 selector & descriptor 的常規(guī)檢查以及權(quán)限檢查。通過之后才真正加載。否則同樣產(chǎn)生 #GP 異常。
情景提示:
- processor 還要做另一項(xiàng)工作,就是:將新進(jìn)程的 TSS descriptor 置為 busy 狀態(tài)。使得新進(jìn)程不能重入。
(7)processor 從當(dāng)前的 CS:RIP 繼續(xù)往下執(zhí)行,完成這個(gè) TSS 進(jìn)程的切換。這個(gè) CS: RIP 就是新加載的新進(jìn)程的 cs : rip
- 從上面的過程可以看出,使用 TSS 進(jìn)程切換機(jī)制異常復(fù)雜,導(dǎo)致進(jìn)程切換的效能太差了。比使用 call gate 以及 trap gate 慢上好多。若當(dāng)中發(fā)生權(quán)限的改變,還要發(fā)生 stack 切換。
- 進(jìn)程的返回同樣復(fù)雜。同樣需要這么多步驟,總結(jié)來說,主要的時(shí)間消耗發(fā)生在新舊進(jìn)程的信息保存方面。
2、 使用 task gate selector
另一種情況是使用 task gate selector 進(jìn)行 TSS 進(jìn)程切換,使用 task gate selector 除了可以 call/jmp 外,還可用在中斷機(jī)制上,下面的兩種情況:
call 0x3b:0x00000000 /* 0x3b 為 task gate selector */
或:int 0x3e /* 假設(shè) 0x3e 是 task gate 的向量 */這兩種情形差不多,除了一些細(xì)微的差別外,不過是觸發(fā)的機(jī)制不同而已。
processor 在 GDT 索引查找到的是一個(gè) task gate descriptor,這個(gè) task gate descriptor 中指出了目標(biāo)的 TSS selector 。processor 從 task gate descriptor 里加載 TSS selector,剩下的工作和使用 TSS selector 進(jìn)行切換進(jìn)程是一致。
processor 訪問 task gate descriptor 僅需對(duì) task gate descriptor 作出權(quán)限檢查:CPL <= DPL 并且 RPL <= DPL。在這里不需要作出對(duì) TSS descriptor 的 DPL 權(quán)限進(jìn)行檢查。
gate descriptor 機(jī)制提供了一層間接的訪問層,主要用來控制權(quán)限的切換。
實(shí)際上:
- 對(duì)于 call 0x2b:0x00000000 這條指令,processor 并不知道這個(gè) selector 是 TSS selector 還是 task gate selector,在查找到 descriptor 后,processor 查看這個(gè) descriptor 的 type 才能確定是 TSS selector 還是 task gate selector。
最后需注意:
- (1)使用 jmp 指令進(jìn)行轉(zhuǎn)移,processor 不會(huì)將 eflags 中 NT 標(biāo)志位置 1
- (2)使用中斷機(jī)制下的 TSS 進(jìn)程切換,processor 將不作任務(wù)的權(quán)限檢查動(dòng)作。
5.7.1.4、 TSS 進(jìn)程的返回
從 TSS 機(jī)制切換的進(jìn)程在執(zhí)行完后使用 iret 指令返回原進(jìn)程進(jìn),同樣會(huì)發(fā)生新舊 TSS segment 的更新動(dòng)作。
進(jìn)程通過使用 ret 返回時(shí),processor 將不會(huì)從嵌套內(nèi)層返回到的嵌套外層進(jìn)程,也就是不會(huì)返回原進(jìn)程。processor 對(duì) ret 指令的處理,只會(huì)從 stack pointer(ss : esp) 取返回地址。
- 對(duì)于使用進(jìn)程使用了 iret 中斷返回指令時(shí):
- (1)processor 將會(huì)檢查當(dāng)前的 eflags.NT 標(biāo)志位是否為 1,也就是檢查當(dāng)前進(jìn)程是否處于嵌套的內(nèi)層。
- (2)eflags.NT = 1,processor 將從當(dāng)前 TSS segment 中的 link(old TSS selector)域中取出原來進(jìn)程的 TSS selector。
- (3)processor 將不作任何的權(quán)限檢查,TSS selector 被加載到 TR.selector,TSS descriptor 同時(shí)被加載到 TR 的隱藏部分。
- (4)processor 將清除當(dāng)前的 eflags.NT 為 0。若是使用 ret 指令返回的,processor 是不會(huì)清 eflags.NT 為 0
- (5)從 TSS segment 中加載新的進(jìn)程執(zhí)行環(huán)境,從新的 CS:EIP 處繼續(xù)執(zhí)行。 將原來的 TSS descriptor 重新置為 available 狀態(tài),使得可以再次進(jìn)入。
- 由上可得,processor 遇到 ret 指令時(shí),是不會(huì)對(duì) eflags.NT 進(jìn)行檢查的。而使用 iret 指令,processor 將對(duì) eflags.NT 進(jìn)行檢查。
總結(jié)
以上是生活随笔為你收集整理的x86 的 TSS 任务切换机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从备案到开机要多久(从备案到开机)
- 下一篇: linux内核用户执行任务效率高还是内核