---------------------------------------------------------------------------------------------
JongXie OS 任務調度的實現
By 薑江 <jznsmail@tom.com>
http://blog.csdn.net/jznsmail
一、概述
=========
JongXie OS Project並沒有採用調度演算法的比較,擇其最優,而是採用了最簡單的時間輪片算
法。之所以採用該演算法是因為我並不想研究各種演算法的效率,而是研究任務是如何來調度的。如果
明白了基本的任務調度原理之後,採用其他調度演算法也並非難事了。
二、任務調度概述
=================
目前的處理器速度可以說是越來越快了,但是外部裝置的速度並沒有伴隨著處理器速度的提高
而提高,相反外部裝置的速度於處理器相比起來是十分慢的。比如說,如果一個系統只支援一個任
務,當該進程需要從外部裝置讀取一些資料,那麼,該進程首先會向CPU發送請求訊號,CPU通知外
部裝置準備,這時CPU什麼都沒幹了,直到外部裝置準備好請求的資料之後發送給CPU一個準備就緒
的回應訊號時,CPU才通知該進程讀取外部裝置的資料。在這段外部裝置的資料準備時間段裡處理器
停滯了。處理器的低效率使得我們會想如何採用一種更好的策略來提高處理器的利用率,這樣就引
入了任務調度的概念。當一個進程請求外部裝置時,可以將該進程先調出休眠,而調度等待隊列裡
的另外一個新進程。在外部裝置準備時間段裡,處理器無須等待,而是利用該段時間來執行新進程。
當原進程所需要的資料準備好之後,處理器將當前執行的進程調出,放入等待隊列裡,然後重新喚
醒原進程取走資料。這樣處理器的利用率隨著空閑等待時間的減少而大大的提高了。隨著任務調度
概念的引入,同樣也引入了多任務作業系統的概念,即處理器可以同時起動多個進程,在宏觀上來
看是同時執行多個進程,但是在處理器微觀角度來看,一個時刻處理器只能執行一個進程,而其他
的進程則放入等待隊列等待調度。
三、時間輪片演算法
=================
時間輪片演算法是由作業系統給每個進程分配一定的時間片段,這個時間片段是任意給的,但是
如果給他大也會造成處理器資源的浪費,如果給太小則會不斷的進行任務的換進換出操作,也大大
降低了處理器的效率。該時間片段對於一個進程來說是處理器調度一次可執行檔時間滴答數,當該
進程的時間滴答數到達最大允許值時,作業系統就會將起調出放入等待隊列,然後重新從等待隊列
裡喚醒一個新的進程進行調度。因為時間片通常都很小,一般是幾十毫秒,因此一秒內多個進程可
能會被交替切換幾此,因此人們感覺多個進程是同時執行的。
四、任務調度的現場保護
========================
因為每個進程對於其他進程來說都是獨立的,它有它單獨的資料區段、程式碼片段、堆棧段等,因此
當時間片輪轉完後,將換入新進程,而被換出進程的資料應該進行現場保護,以便下次喚醒時繼續
執行。這跟中斷處理常式的保護現場操作有點類似。任務調度的現場保護通常是儲存當前即將換出
進程的一些處理器狀態資訊,而對於作業系統的設計來說,這些狀態資訊的集合就構成了我們通常
所說的任務狀態結構(TSS)。
在設計JongXie OS的時候,我參看了Linux的原始碼,在JongXie OS項目開發中,採用了類Linux
的任務狀態結構。下面是TSS的結構圖:
31 15 0
+--------------------------------------------+---------------------------------------------+
| Reserved(NULL) | Link(previous task segment descriptor) |
+--------------------------------------------+---------------------------------------------+
| *ESP0(Super privilege ESP pointer) |
+--------------------------------------------+---------------------------------------------+
| Reserved(NULL) | *SS0(Super privilege SS pointer) |
+--------------------------------------------+---------------------------------------------+
| *ESP1(Super privilege ESP pointer) |
+------------------------------------------------------------------------------------------+
| Reserved(NULL) | *SS1(Super privilege SS pointer) |
+--------------------------------------------+---------------------------------------------+
| *ESP2(Super privilege ESP pointer) |
+------------------------------------------------------------------------------------------+
| Reserved(NULL) | *SS2(Super privilege SS pointer) |
+--------------------------------------------+---------------------------------------------+
| *CR3(Page Directory Base Rgiseter) |
+--------------------------------------------+---------------------------------------------+
| EIP(Program pointer regiseter) |
+--------------------------------------------+---------------------------------------------+
| EFLAGS(CPU status register) |
+--------------------------------------------+---------------------------------------------+
| EAX |
+--------------------------------------------+---------------------------------------------+
| ECX |
+--------------------------------------------+---------------------------------------------+
| EDX |
+--------------------------------------------+---------------------------------------------+
| EBX |
+--------------------------------------------+---------------------------------------------+
| ESP |
+--------------------------------------------+---------------------------------------------+
| EBP |
+--------------------------------------------+---------------------------------------------+
| ESI |
+--------------------------------------------+---------------------------------------------+
| EDI |
+--------------------------------------------+---------------------------------------------+
| Reserved(NULL) | ES |
+--------------------------------------------+---------------------------------------------+
| Reserved(NULL) | CS |
+--------------------------------------------+---------------------------------------------+
| Reserved(NULL) | SS |
+--------------------------------------------+---------------------------------------------+
| Reserved(NULL) | DS |
+--------------------------------------------+---------------------------------------------+
| Reserved(NULL) | FS |
+--------------------------------------------+---------------------------------------------+
| Reserved(NULL) | GS |
+--------------------------------------------+---------------------------------------------+
| Reserved(NULL) | *LDT Selector Descriptor |
+--------------------------------------------+---------------------------------------------+
| *I/O bit map | Reserved(NULL) |
+--------------------------------------------+---------------------------------------------+
31 15 0
上面的結構中帶有'*'號的不分是處理器唯讀,而其他部分是處理器來填寫的,當然要除了
Reserved部分,Reserved部分是不會使用的。處理器在進行任務切換的過程中,會自動的填寫沒有
'*'號的內容,這樣一個TSS結構就儲存了當前任務的所有必須的相關資訊了。唯讀部分是說處理器
在任務切換時會從中讀取相關資訊,但是並不會在任務切換出去的時候儲存它們。它們是在系統創
建任務的時候就已經指定好了的。在JongXie OS項目中,任務的切換是通過System_Timer中斷處理
程式來處理的,它完成了處理器資訊的儲存設定工作和任務的切換工作。相關代碼可以查看int.asm
檔案。
五、TSS描述負和任務門
=======================
TSS結構是我們通過編成語言來構造的一個邏輯結構,但是處理器並不能直接儲存該結構,需要
用到TSS描述負和任務門來儲存指向TSS結構的一個指標。
TSS描述符結構:
15 7 0
+--------------------------------+--------------------------------+
| TSS Limit 0 ~ 15 bits |
+--------------------------------+--------------------------------+
| TSS Base Address 0 ~ 15 bits |
+--------------------------------+--------------------------------+
| P | DPL | 0 | 1 | 0 | B | 1|TSS Base Address 16 - 23 bits|
+--------------------------------+--------------------------------+
|TSS Base Address 24 - 31 bits| G | 0 | 0 |AVL|Limit16-19bits|
+--------------------------------+--------------------------------+
B:標誌任務是否忙
DPL:特權級(2bits)
P:標誌TSS是否存在在記憶體中
G:粒度位,在32位保護模式下應該為1
從TSS結構圖中我們可以看出,TSS描述符指代了一個TSS結構,對於一般任務來說這個描述符已
經夠了,但是INTEL公司在設計處理器的時候允許在中斷過程中進行任務切換,因此我們可以將中斷
處理常式作為一個專門的任務。但是中斷描述符表中並不允許存放TSS描述符,而只能是門描述符,
因此TSS描述符不能儲存在中斷描述符,INTEL為此定義了一個任務門。任務門指向的是一個TSS描述
負,當中斷產生時處理器通過中斷號查詢中斷描述符表,擷取相應的門描述符,如果擷取的是一個任
務門,那麼將通過它找到相應的TSS描述符,然後再通過相應的TSS描述符尋找到對應的TSS結構。下
圖就是任務門的結構圖:
15 7 0
+------------------------------+-----------------------------+
| Reserved(NULL) |
+------------------------------+-----------------------------+
| TSS Selector |
+------------------------------+-----------------------------+
| P|DPL |0 | 0 | 1 | 0 | 1 | Reserved(NULL) |
+------------------------------+-----------------------------+
| Reserved(NULL) |
+------------------------------+-----------------------------+
DPL:特權級(2bits)
P:標誌TSS是否存在在記憶體中
作業系統中,TSS描述符放在通用描述元表中,因為它將會被處理器、中斷服務程式、其他進程
訪問。TSS選擇符的作用就是在通用描述元表中索引找到相應的TSS描述符。
六、任務切換
=============
系統的GDT通用描述元表儲存著TSS描述符和任務門,任務門是一個指向TSS描述符在GDT表中的索
引值,而TSS描述符指向的是一個TSS結構。同樣在中斷描述符表中也存在一個指向TSS描述符的任務
門。這樣,有3種情況可能會引起任務切換:1.當使用了JMP或者CALL指令,並且指令的目標地址是指
向一個TSS描述符,或者指向一個任務門描述符,而該任務門描述符指向了一個TSS描述符時會引起任
務切換。2.產生中斷調用,中斷向量指向了中斷向量表中的一個任務描述符時也會引起任務切換。3.
使用了IRET指令,並且在執行該指令的時候EFLAGS寄存器NT位被置位1時,同樣也會引起任務切換。
下面我分這幾種情況來討論任務切換:
1.當作業系統通過一個JMP跳轉指令跳轉到一個TSS描述符,處理器將檢查該描述符的特權級,和
B位是否為0,如果為0,那麼處理器將目標任務的B位置1表示忙,然後把當前任務的B位置0。之後處理器
會進行任務切換的現場保護,將處理器狀態資訊放入相應的TSS結構中,並且從目標任務的TSS結構中
取出相應的資訊,完成一次任務切換。
2.當作業系統通過CALL指令或者中斷來切換任務,處理器將檢查特權位和B位,然後還會將目標任
務的EFLAGS的NT位置1,並且將當前任務的TSS描述符放入目標任務Link欄位中,之後,處理器把當前任
務的狀態資訊放入相應的TSS結構中來保護現場,並且從目標任務的TSS結構中取出需要的資訊,完成
任務切換。
3.如果當前任務切換是由於IRET指令引起的,並且當前任務的EFLAGS的NT位是置1的,那麼處理器
會依次完成特權檢查,B位檢查後,將當前任務的B位清0,並且將當前任務的EFLAGS的NT位清0,然後處理
器將當前任務資訊保護起來,放入TSS結構中,並且取出目標任務TSS結構中所需要的資訊,完成任務
切換。
七、參考文檔
1.Intel 80386 Programmer's Reference
2.Linux source code