本節書摘來華章計算機出版社《linux裝置驅動開發詳解 a》一書中的第1章,第1.2節,作者:宋寶華 更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。1
并不是任何一個計算機系統都一定要有作業系統,在許多情況下,作業系統都不必存在。對于功能比較單一、控制并不複雜的系統,譬如asic内部、公共汽車的刷卡機、電冰箱、微波爐、簡單的手機和小靈通等,并不需要多任務排程、檔案系統、記憶體管理等複雜功能,用單任務架構完全可以良好地支援它們的工作。一個無限循環中夾雜着對裝置中斷的檢測或者對裝置的輪詢是這種系統中軟體的典型架構,如代碼清單1.1所示。
代碼清單1.1 單任務軟體典型架構
1?int main(int argc, char* argv[])
2?{
3? while (1)
4? {
5? if (serialint == 1)
6? / 有序列槽中斷 /
7? {
8? processserialint(); / 處理序列槽中斷 /
9? serialint = 0; / 中斷标志變量清0 /
10? }
11? if (keyint == 1)
12? / 有按鍵中斷 /
13? {
14? processkeyint(); / 處理按鍵中斷 /
15? keyint = 0; / 中斷标志變量清0 /
16? }
17? status = checkxxx();
18? switch (status)
19? {
20? ...
21? }
22? ...
23? }
24?}
在這樣的系統中,雖然不存在作業系統,但是裝置驅動則無論如何都必須存在。一般情況下,每一種裝置驅動都會定義為一個軟體子產品,包含.h檔案和.c檔案,前者定義該裝置驅動的資料結構并聲明外部函數,後者進行驅動的具體實作。譬如,可以像代碼清單1.2那樣定義一個序列槽的驅動。
代碼清單1.2 無作業系統情況下序列槽的驅動
1? /
2? *serial.h檔案
3? /
4? extern void serialinit(void);
5? extern void serialsend(const char buf*,int count);
6? extern void serialrecv(char buf*,int count);
7?
8? /
9? *serial.c檔案
10? /
11? / 初始化序列槽 /
12? void serialinit(void)
13? {
14? ...
15? }
16? / 序列槽發送 /
17? void serialsend(const char buf*,int count)
18? {
19? ...
20? }
21? / 序列槽接收 /
22? void serialrecv(char buf*,int count)
23? {
24? ...
25? }
26? / 序列槽中斷處理函數 /
27? void serialisr(void)
28? {
29? ...
30? serialint = 1;
31?}
其他子產品想要使用這個裝置的時候,隻需要包含裝置驅動的頭檔案serial.h,然後調用其中的外部接口函數。如要從序列槽上發送“hello world”字元串,使用語句serialsend(“hello world”,11)即可。
由此可見,在沒有作業系統的情況下,裝置驅動的接口被直接送出給應用軟體工程師,應用軟體沒有跨越任何層次就直接通路裝置驅動的接口。驅動包含的接口函數也與硬體的功能直接吻合,沒有任何附加功能。
圖1.1所示為無作業系統情況下硬體、裝置驅動與應用軟體的關系。
有的工程師把單任務系統設計成了如圖1.2所示的結構,即裝置驅動和具體的應用軟體子產品之間平等,驅動中包含了業務層面上的處理,這顯然是不合理的,不符合軟體設計中高内聚、低耦合的要求。
另一種不合理的設計是直接在應用中操作硬體的寄存器,而不單獨設計驅動子產品,如圖1.3所示。這種設計意味着系統中不存在或未能充分利用可重用的驅動代碼。