羅 斌
【摘要】文章介紹了一個基于PC體系結構的嵌入式操作系統,從CPU的16位實模式切換到32位保護模式,包括中斷控制器、系統時鐘等在內的系統硬件初始化過程,全局描述符表、中斷描述符表等內核運行環境的建立,進程的定義以及調度算法,進程切換方面介紹了一個具備基本功能的嵌入式操作系統。
【關鍵詞】PC;嵌入式操作系統;CPU保護模式
當前,嵌入式操作系統已經獲得非常廣泛的應用,涉及的領域包括通信、軍事、航空航天、以及生活等方方面面。目前市場上比較有名且占有一定市場分額的操作系統有VxWork、uLinux、uC/OS-II、eCos等,他們有各自的優點,VxWork經過了長時間的發展和實際應用的考驗,在關鍵應用領域仍是一支獨秀。uLinux則可以利用廣泛的Linux資源,便于開發等等。
由于操作系統的基本功能是硬件無關的,因此這部分可以使用C語言進行開發,而平臺相關的部分只需要使用匯編語言,但是只要設計好代碼框架,就可以做到方便移植。選擇了PC作為最初的目標平臺,是因為x86體系結構應用范圍越來越廣,而且可以在開發x86版本后再移植到其他平臺。
一、操作系統簡介
操作系統在功能上主要是負責管理計算機的資源,這些資源包括CPU、內存、外部存儲器、還有一些其他的外設。
從結構上則分為單一內核和微內核結構,單一內核的代表就是UNIX、Linux,這類操作系統的內核是一個整體,功能都包含在內核里面,有點“大雜燴”的味道,其優點就是由于功能都包含在內核中,因此內核很緊湊,且效率較高。而且帶來的缺點就是當一個功能模塊需要修改時,就必須將內核全部編譯一次。微內核結構的代表就是windows系列,其內核提供的功能很少,僅有一些基本的功能,其他的功能全部作為擴展模塊連接進入核心,這樣的好處就是各個功能模塊在接口不變的時候,可以很方便的更新而不影響系統,給系統提供了最大的靈活性。帶來的缺點是結構不夠緊湊,造成一定的資源浪費,同時也對運行效率產生一定影響。本系統采用的是單一內核結構。
操作系統的開發通常是在UNIX、Linux下使用GNU Compiler Collection(GCC)進行開發編譯。一般認為不能在Windows下開發,并且認為微軟提供的開發工具不能進行此類開發。但是本操作系統是在Windows下開發使用Microsoft Visual C/C++ 6(VC6)進行開發。
二、CPU保護模式初始化
PC在通電后運行于16位實模式,其尋址能力為64K,而嵌入式系統的配置范圍非常廣泛,僅僅考慮16位的尋址范圍顯然不適合當前的發展要求。而i386及其以上的CPU的尋址能力都達到了4G,因此需要設計能支持4G內存的操作系統。
在系統引導時,首先建立一個臨時的全局描述符表(GDT),這個全局描述符表中有5個描述符,第一個必須設為0,這是x86處理器規定的,稱為啞描述符。其中兩個是16位保護模式代碼段和數據段的描述符,用于在切換CPU運行模式的時候,保證初始化代碼仍然能夠正常運行。另外兩個是32位保護模式下的代碼段和數據的描述符,用于CPU內核運行。
由于CPU在復位時是處于16位實模式,所以初始化代碼是16位實模式下的代碼,這部分的代碼的作用是為切換到16位保護模式準備運行環境,主要的工作是設置GDT。具體地做法是根據當前的段寄存器計算GDT中相應段描述符的值。
再設置好GDT后,就可以將CPU切換到保護模式下運行。將CPU切換到保護模式的方法是,將cr0寄存器的0位設置為1,代碼為:
……
mov eax,cr0
or eax,1
mov cr0,eax
……
再切換到保護模式運行后,由于CPU有指令預取機制,所以在指令隊列中已經存在了下一跳指令,而且需要刷新代碼段寄存器,為了防止運行出錯,必須刷新指令隊列已近代碼段寄存器。由于需要在一條指令中同時刷新段寄存器和指令隊列,所有需要手動構造一條段間轉移指令。代碼為:
……
db0eah
dwoffset init
dwtemp_code_sel
init:
……
完成后CPU即進入16位保護模式運行。
三、建立內核運行環境
進入內核后,首先必須建立運行環境,對于INTEL的CPU來說,就是建立兩個系統表,一個是全局描述符表(GDT),一個是中斷描述符表(IDT)。
(一)全局描述符表初始化
在CPU由16位實模式切換到32位保護模式的時候,建立了一個臨時的GDT。但是進入內核后,需要一個真正的描述符表,不能在繼續使用這個臨時的GDT,否則在以后的任務切換中將會遇到麻煩。
由于假設系統運行在單一地址空間,因此全局描述符表僅需要3個描述符,可以使用數組定義如下:
DESC gdt[4] = { {0,0,0,0,0,0},
{0xffff,0x0000,0x00,0x9a,0xcf,0x00}, // 代碼段
{0xffff,0x0000,0x00,0x92,0xcf,0x00} // 數據段
};
定義了新的全局表述附表后,就可以將GDT更換為新的描述符表,并且將段寄存器CS的值更換為新的段選擇子的值。
(二)中斷描述符表初始化
運行環境的另一個重要的系統表就是中斷描述符表,他決定了由哪個程序來響應系統的中斷或者陷阱。在保護模式初始化時,并沒有對IDT進行初始化,甚至沒有定義中斷描述符表,那是因為在進行保護模式切換時,可以保證系統運行于關中段狀態,保證初始化本身也不使用任何的軟中斷指令,這樣就可以在沒有對IDT初始化的情況下,確保不會發生因中斷描述符表沒有設置而導致的錯誤。
由于INETL的CPU可以響應256個中斷,因此系統IDT最大有256個描述符,但是實際系統中并不需要那么多,所以可以根據需要進行設置,在本系統中,為了節約空間,僅定義了64個。
定義后并將其初始化,使用一個默認過程來初始化,這個默認的過程什么工作都不作,僅僅是簡單的返回。默認過程定義如下:
__declspec( naked )
void default_int()
{
__asm iretd ;
}
這里使用了__declspec( naked )這個由VC6提供的特有關鍵字,它可以讓編譯器不生成函數的環境初始化代碼,所有的操作都交由程序員來完成。因此可以使用VC6來編寫中斷處理程序,這也是為什么選擇VC6來編寫這個操作系統的原因。
(三)系統基本硬件初始化
系統環境建立后,需要對硬件設備進行初始化,為了保證通用性,僅初始化大多數系統都會有的硬件,就是系統時鐘。
在PC系統中要初始化系統時鐘,必須首先初始化中斷控制器,因為系統時鐘是通過中斷控制器接入系統的。由于INTEL已經保留了CPU的0-31號中斷,因此中斷控制器的中斷只能從32號中斷開始。在本系統中,中斷控制器接入CPU的中斷號從32開始,到47,一共16個,另外保留16個中斷門作為系統調用的入口,為以后擴展功能留下一定的余地。
四、 進程定義及調度
(一)進程定義
任何操作系統,最重要的概念就是進程,進程通常表示一個執行的程序,它包括可執行的程序、程序數據、棧、程序計數器等等程序運行需要的信息。在本系統中,由于并沒有涉及復雜的功能,所以進程結構的定義比較簡單。僅包含調度相關的信息和一些基本進程信息
struct _task
{
struct _node task_node;
char task_cputime;
BYTEtask_stat;
BYTEtask_pri;
BYTEtask_pril;
DWORD task_prinum;
int task_sleep;
void *task_sp;
char *task_name;
int task_id;
int task_pid;
unsigned int task_errcode;
};
(二)調度算法
本系統采用的調度算法是以優先級為主,輔以時間片輪轉的調度算法。以優先級為主的調度算法,可以保證高優先級的進程可以搶占CPU,以保證實時任務得到及時響應,而輔以時間片輪轉,則可以保證優先級相同時,各進程公平的獲得CPU時間。
系統調度過程是調度器根據優先級從高到低的原則掃描系統進程表,在發現某個優先級存在可調度進程的時候,就轉入掃描該優先級的進程表,如果該優先級中存在多個任務,就根據時間片輪轉的原則挑選出進程,然后將CPU的控制權交給挑選出來的進程。
五、結語
這僅僅是一個最簡單的操作系統內核,沒有提供任何額外的系統服務,包括一些類似系統延時、互斥對象、定時器這樣的基本功能,但是有了基本的內核后,就可以在其上發展,使其逐漸發展壯大。
【參考文獻】
[1]John Lions. Lion's Commentary on UNIX 6th Edition with Source Code [M].機械工業出版社,2000.
[2]Andrew S. Tanenbaum. Modern Operating Systems [M].機械工業出版社,1999.
[3]Jean J. Labrosse. MicroC/OS_II The Real-Time Kernel Sec-
ond Edition [M].北京航空航天大學出版社,2003.
[4]楊季文.80X86匯編語言程序設計教程[M].清華大學出版社,1998.
【作者簡介】羅斌(1978- ), 男 ,廣西馬山人,廣西電網公司南寧供電局助理工程師。