5.1設計目的
OSKit是一個用來幫助我們研發操作系統的工具,如果你想更好的掌握它,除了對其本身進行分析之外,還有一個更加實際的方法,那就是利用這個工具開發一個我們自己的操作系統。
我們開發這個系統的初衷主要是為了學習,因此並沒有想把它設計成像Windows、UNIX那麼復雜。我們的目標是很明確的,那就是在大致搞明白OSKit應當如何使用之後,做一次嘗試,用較短的時間,設計出一個具有一些最基本功能的系統。這對於我們課題組的每名成員來說,都意味著挑戰。
5.2系統的功能
這個系統支持些什麼?這是在開始設計之前我們要首先確定的一個問題。下面列出了一些我們希望實現的功能。
通過Multiboot或者LILO進行系統引導:這個系統可以通過所有支持Multiboot的引導程序進行引導,或者通過引導Linux的LILO進行引導。引導方式可以根據用戶的要求,選擇是通過軟盤還是硬盤。
多線程:用戶可以通過我們提供的系統調用,創建或殺死自己的線程。由於並不支持文件系統,因此新的線程恐怕只能是一個函數了。從理論上講,我們可以讓這個系統支持很多線程,但因為這僅僅是一個簡單的演示性系統,因此我們把線程數限制在32個,當然這個數字也可根據用戶的需求任意更改。
進程間通信:這個系統可以支持一些簡單的進程間通信機制,比如信號量、消息隊列等等。
外設:在眾多的外設之中,我們選擇了應用范圍比較廣的串口。利用目前OSKit所提供的機制,支持更多的設備是不成問題的,但加在這個系統中意義並不十分的大。另外,需要強調的是,訪問串口的方法,是由我們自己規定,不允許用戶更改。
簡單的內存管理:應用程序需要的內存可以通過malloc函數動態申請。
總體構想
在該操作系統中,我們規定用戶只能創建用戶級線程,所有對於核心的操作都將由系統來完成。線程采用RR調度算法,也就是時間片輪轉法,並允許搶占,線程在該系統中有四種狀態,它們是運行態、阻塞態、睡眠態和就緒態,用戶可以設定和改變線程的優先級。在線程中用戶可以動態申請內存,但是需要由用戶來釋放。線程間提供簡單的通信機制,包括信號量和消息隊列。
在設備方面,對於終端,本系統將提供簡單的輸入輸出功能。而對於串口的功能,本系統可以完整的實現,也就是說,用戶既可以從串口讀也可以向串口寫。
為了方便起見,我們為這個系統起一個很簡單的名字,叫acos,這個名字並沒有什麼實際的意義,僅僅為了叫起來方便。
雖然OSKit已經為我們提供了很多設計操作系統所必須的功能,比如內存分配中的malloc函數,通過Multiboot引導等等。然而,仍有許多工作是要由我們自己完成。
5.3 我們自己所完成的工作:
5.3.1 系統的啟動
這裡我們所說的系統啟動,並不是指通常人們所說的通過軟盤或硬盤引導,而是在引導結束之後,操作系統的核心進程如何啟動第一個用戶進程。通常,在UNIX系統中,內核啟動之後,內核程序會去執行文件系統上的init程序,這個程序是整個操作系統中的第一個用戶進程,剩下的進程,都必須通過這個進程才能啟動。在acos中,我們規定,用戶必須要提供一個acos_init( ) 函數,系統在啟動初始化工作全部完成之後,acos_init( )函數將被作為系統中的第一個進程啟動。這之後的工作,就完全由用戶來完成了。
當進程acos_init( ) 結束時,整個系統也將終止運行。
在acos_init( )啟動之前,究竟還有哪些事情需要做呢?下面我們就列出要做的事情:
對整個系統進行初始化,注冊設備,檢測設備,初始化設備需要的數據結構,初始化線程庫,創建線程需要的數據結構。
5.3.2 線程管理
所謂操作系統的線程管理,一般包括了一下的三大部分:線程的創建,線程間通信和線程調度。
線程的創建包括了初始化線程和創建線程的屬性。
線程間通信中,我們使用了信號量,條件變量。
在線程調度中,用戶線程只能使用時間片輪轉法作為調度算法,並且規定在調度過程中調度算法不允許更改。
5.3.3 外設(串口)
在UNIX系統中,所有的設備都可以通過文件系統進行訪問,讀、寫設備就像讀寫文件一樣。在我們設計的這個acos中,並沒有對文件系統提供支持,也不提供像讀寫文件系統一樣的方法來訪問設備。但是,我們提供一套基本的函數,供用戶線程對串口進行讀寫操作,這些函數專門用於處理串口。
一個設備在同一時刻只能被一個線程使用,因此,在使用設備以前要申請,只有申請成功的線程才能夠使用設備。
5.4 用戶手冊
5.4.1 安裝
用戶需要在Linux下鍵入 tar -zxvf acos-0.0.1.tar.gz 。
./setup ,詳細的安裝信息請參見README文件。
進入usr/local/bin/acos之後,根據我們所提供的API手冊編寫自己的應用程序,然後將這些應用程序的源文件編譯成一個"filename.o",用戶通過執行acbuild filename.o命令將應用程序鏈入操作系統內核,根據你所使用的核心格式執行mkXXXimage命令生成所需要的核心映像,選擇恰當的裝入程序裝入啟動核心映像。
5.4.2 用戶編程接口
如果您對操作系統的概念有所了解,並詳細閱讀了我們為您提供的技術文檔之後,便可以利用用戶編程接口自行開發了。以下我將向各位詳細介紹每個接口所提供的功能,希望對您能有所幫助。
一、管理線程的API
1.1 初始化線程
ac_init_pthreads( );
1.2 線程創建
ac_thread_create ( void *name, void *arg,
int *out_thread_id );
1.3 設置線程的優先級
ac_thread_setprio(int thread_id, int pri);
1.4 撤銷線程
ac_thread_cancel(int thread_id);
1.5 線程休眠
ac_thread_sleep(ac_s64_t millisecond);
1.6 將該線程與某一個線程相關聯
ac_thread_join(int tid, void **status);
1.7 撤銷該線程與某線程的關聯
pthread_attr_setdetachstate ( pthread_attr_t *attr, int detachstate )
二、管理互斥量的API
2.1 初始化互斥量
ac_mutex_init ( ac_pthread_mutex_t* mutex,
ac_pthread_mutexattr_t* attr);
2.2 設置互斥量協議
ac_mutexattr_setprotocol ( ac_pthread_mutexattr_t* attr, int protocol);
2.3 設置互斥量類型
ac_mutexattr_settype ( ac_pthread_mutexattr_t* attr, int type);
2.4初始化互斥量屬性
ac_mutexattr_init(ac_pthread_mutexattr_t* attr);
2.5互斥量加鎖
ac_mutex_lock(ac_pthread_mutex_t *mutex);
2.6互斥量解鎖
ac_mutex_unlock(ac_pthread_mutex_t *mutex);
2.7撤銷互斥量
ac_mutex_destroy(ac_pthread_mutex_t *mutex);
三、管理IPC的API
3.1消息發送( IPC機制 )
ac_ipc_send ( int thread_id, void* msg,ac_size_t msgsize,ac_s32_t timeout);
3.2消息接受( IPC機制 )
ac_ipc_recv(int thread_id, void* msg,ac_size_t msgsize,ac_size_t *actual, ac_s32_t timeout);
3.3消息等待( IPC機制 )
ac_ipc_wait(int *src_id, void *msg,ac_size_t msgsize,ac_size_t *actual, ac_s32_t timeout);
3.4接受同步消息
ac_ipc_reply(int desthread, void *msg, ac_size_t msg_size);
3.5發送同步消息
ac_ipc_call(int dst, void *send_msg, ac_size_t send_msg_size,
void *recv_msg, ac_size_t recv_msg_size,
ac_size_t *actual,ac_s32_t timeout);
四、管理條件變量的API
4.1條件變量初始化
ac_cond_init(ac_pthread_cond_t *cond, ac_pthread_condattr_t* attr);
4.2某線程等待條件變量
ac_cond_wait(ac_pthread_cond_t *cond, ac_pthread_mutex_t *mutex);
4.3喚醒等待該條件變量的線程
ac_cond_signal(ac_pthread_cond_t *cond);
4.4符合條件變量的線程廣播
ac_cond_broacast(ac_pthread_cond_t *cond);
五、硬件部分
5.1打開串口
ac_serial_open(int serial_num, ac_u32_t flags);
5.2監聽串口
ac_serial_listen(int serial_num, ac_u32_t flags);
5.3關閉串口
ac_serial_close(ac_ttystream_t* a_stream);
5.4從串口讀
ac_serial_read(ac_ttystream_t* s, void *buf, ac_u32_t len,ac_u32_t *out_actual);
5.5往串口寫
ac_serial_write(ac_ttystream_t* s, const void *buf,ac_u32_t len, ac_u32_t *out_actual);
5.4.3 應用程序示例
為了能讓各位老師對我們的演示系統一目了然,我們特地選取了在操作系統中比較有代表性的4個例子供大家欣賞,它們是,哲學家算法,時鐘演示,線程調度和中斷響應。
所需的資源:演示前三個例子需要一台intel 386或更新的計算機,一張1.44MB的軟盤;演示第四個例子除了上述設備之外,還需要一根串口線,一台裝有Windows 9x的計算機作發送機,並在其上安裝有能向串口發送信息的軟件。
1.哲學家算法
哲學家算法也稱哲學家進餐的同步問題,最早是在1965年由Dijkstra提出並解決的。下面我就簡單描述一下這個經典IPC問題的大意:7個哲學家圍坐在一張圓桌周圍,每個哲學家面前有一個碟通心面,由於面條很滑,所以要用兩把叉子才能夾住。相鄰兩個碟子之間有一把叉子。哲學家的生活包括三種狀態:即吃飯,思考和挨餓,當一個哲學家覺得餓時,他試圖分兩次去取他左邊和右邊的叉子,每次拿一把,但不分次序。如果成功地獲得了兩把叉子,他就開始吃飯,吃完以後放下叉子繼續思考。這裡的問題是:為每一個哲學家寫一段程序來描述起行為,要求不能死鎖。
在這個例子中我們設定了7個哲學家,且不允許哲學家餓死。我們為每個哲學家創建一個線程,把叉子看作條件變量,讓線程等待條件變量,如果滿足條件,則線程執行(哲學家的吃飯狀態),執行一定時間之後,釋放條件變量,進入休眠狀態(哲學家的思考狀態),當休眠時間過去之後,線程被喚醒,重新等待條件變量(哲學家的挨餓狀態)。
2.時鐘演示
計算機是以嚴格精確的時間進行數值運算和數據處理的,最基本的時間單元是時鐘周期,OSKit最小可以精確到納秒一級,其時鐘系統的特點是允許在系統時鐘之上創建自己的時鐘,用於控制周期性線程。
我們的演示系統的源代碼看似比較簡單,但實際上卻是建立在一個龐大的時間系統之上的,這個時間系統是整個操作系統活動的動力。
我們的演示程序將演示控制計算機的時鐘計時( 每400毫秒顯示一次 ),計時10次後返回初始菜單。
3.線程調度
在此個演示程序中,一開始有兩個線程,它們所采取的調度算法是時間片輪轉法,與此同時還有一個優先級最高的線程在休眠,它每400毫秒睡醒一次,打斷此時處於運行狀態的線程,那個優先級最高的線程轉執行態,等它執行完它的時間片之後,前兩個線程繼續執行。
4.中斷響應
這個例子演示了操作系統對外設的支持,我們選擇了串口。演示這個例子,需要兩台計算機和一根串口線,其中一台計算機安裝我們的演示系統,另一台需要有一個向串口發送信息的軟件,此軟件我們是用Delphi編寫的,運行在Windows 98系統下,兩台計算機用我們自己制作的串口線相連。演示程序的一開始,有兩個優先級相同的線程在時間片輪轉。當我們從另一台裝有Windows 98的機器上給演示機發送信息時,原來處於運行狀態的兩個線程被中斷,演示機會成功的收到發送機發送的信息,然後等待操作人員的處理,是否繼續運行該例子或者退出。
本 章 小 結
本章是為最後提供給各位老師欣賞的演示系統所寫的,針對每個例子,先從理論上論述了它的概念以及我們是怎麼實現的,然後附上了部分程序。
注意:由於演示系統所使用的OSKit的系統調用基本在前三章已經逐一做了詳細的介紹。