汪曉兢
(中國航天科工集團第三研究院 總體設計部,北京 100074)
RTX51Tiny是應用于8051系列單片機的小型多任務實時操作系統(tǒng),它完全集成在Keil C51編譯器中,體積小,簡潔高效,能夠實現(xiàn)獨立不相關的多任務并發(fā)執(zhí)行,具體相關伙伴組任務之間的同步機制。被廣泛應用在8051系列單片機的軟件開發(fā)項目中。
當Tiny系統(tǒng)打開時間片(timeshare≠0)時,系統(tǒng)中多任務由內核按時間片輪轉調度,并發(fā)運行。但這時只有獨立不相關任務能夠按時間片并發(fā)推進;而對于有共享資源關聯(lián)的(非伙伴組之間的)相關任務,Tiny卻缺少對共享資源爭用的管理工具。信號量是一種有效的、對共享資源進行保護的管理工具;管程是具有同等表達能力的另一種高級同步管理工具。
信號量實現(xiàn)兩種管理機制:互斥和同步,兩者完全由程序員負責。
管程相應實現(xiàn)兩種管理機制:互斥鎖和條件變量。理論上編譯器負責互斥自動加鎖部分,條件變量部分仍需程序員負責,這使程序員出錯幾率減少一半。管程機制的目標是為了更易于編寫正確的程序,管程相對信號量更易于檢錯和控制。
信號量設計方法見參考文獻[1]。信號量的優(yōu)點是通用性,可以做成庫函數(shù),一次設計,長久應用。而管程,理論上是編譯器依賴,與條件變量一起,隨具體項目設計而不同。
Keil C51沒有管程支持,影響管程C語言應用的原因大多是一種習慣概念:“管程是語言概念,C語言并不支持它。……要使用管程,就需要一種帶有管程的語言?!睂嶋H上,對Tiny這樣的小系統(tǒng),富有好奇心和探索欲的程序員可以自己代勞,替代編譯器完成管程結構成分的功能。理論上進入管程時的互斥由編譯器負責,而通常的做法是用一個互斥鎖或二元信號量。當一個任務調用管程過程時。該過程中的前幾條指令將檢查在管程中是否有其他的活動任務。這是程序員替代編譯器完成管程語言成分時所需做的少量工作。
管程結構如圖1所示,管程把所有共享資源以及一組針對這些共享資源的操作過程集中在一個軟件模塊Monitor內部。并發(fā)爭用管程內部共享資源的外部多任務,只能通過調用管程內部過程來間接使用管程內部共享資源。管程的互斥特性保證了外部無序并發(fā)任務只能以串行化方式順序通過管程(臨界區(qū)),以協(xié)作方式順序使用共享資源。管程結構(見圖1)的右半部實現(xiàn)互斥機制;左半部實現(xiàn)條件變量同步機制。
下面舉實際應用中的例子簡單說明管程功能及應用。
例1 無條件臨界區(qū) (實現(xiàn)圖1右半部互斥機制)
假定有三個RTX51-TNY任務,task1~task3,并發(fā)使用單一串口資源分別輸出數(shù)據(jù)s1~s3,這里串口是共享資源。
圖1 管程結構
編者注:例1源程序略。
三任務借助回調函數(shù)方法進入管程 Monitor_f((*f)(void*),void*),而管程函數(shù) Monitor_f()在入口處添加了一把互斥鎖mutex,使管程函數(shù)具有互斥特征,每次只允許一個申請使用管程內共享資源的外部活動任務進入管程。
申請進入管程的任務,必須先拿到互斥鎖mutex,獲得使用權,使用完資源后,退出管程前又必須先歸還鎖,釋放使用權。獲取鎖成功的任務,必須按照“以關鎖操作開始,以開鎖操作結束”的規(guī)則進行。
互斥鎖是信號量鎖的簡化應用,差別在于:當取鎖失敗時,管程互斥鎖調用os_switch_task()調度程序,主動放棄CPU給下一任務,并不阻塞當前任務,仍為就緒態(tài),因此無需等待其他任務喚醒,也不像自旋鎖那樣忙等待;在該任務下次被調度時,它再一次對鎖進行測試。
當取鎖成功時,互斥鎖能夠自動關鎖以防止其他任務進入;這避免了程序員“以關鎖操作開始”的疏忽。
例2 條件臨界區(qū)
例1中,管程提供了一種實現(xiàn)互斥的簡便途徑;如果僅僅替代編譯器的互斥加鎖功能,這好像已經夠用了。但管程還需要有同步機制,還需要一種方法使進入管程的任務能夠像使用信號量一樣實現(xiàn)同步機制。實現(xiàn)前驅或資源條件臨界區(qū),即進入管程的任務在發(fā)現(xiàn)繼續(xù)運行條件不滿足時,能夠阻塞自己。這是應該由程序員主要負責的部分,編譯器無能為力。
解決方法就是引人條件變量以及利用RTX51Tiny已有的相關的兩個操作:os_wait()和os_send_signal()。當一個管程過程發(fā)現(xiàn)它無法繼續(xù)運行時(例如前驅條件或者資源不滿足時),它會在某個條件變量上執(zhí)行os_wait()操作。該操作導致調用任務自身阻塞,并且還將另一個以前等在管程之外的任務調入管程。一個進入管程無法繼續(xù)運行,被迫進入自身阻塞(等待條件滿足)的任務,必須按下列規(guī)則進行:
◆釋放互斥鎖給管程入口;
◆進入條件等待隊列;
◆睡眠。等待伙伴喚醒。
假定例1中三個任務要求按圖2所示邏輯規(guī)則順序輪流使用串口資源輸出,管程內部就需要有條件變量支持。條件變量同步機制理論上是由程序員負責的部分,編譯器(替代)仍只負責互斥鎖部分。
編者注:例2源程序略。
這個例子當然有更簡單的解法。測試流程如圖3所示,圖中寫成完整if嵌套形式,是為了突出條件變量的邏輯表達式概念。退出管程前的任務測試程序,代碼已經裁剪掉一半(在等待隊列初始化全0條件下)。任務1和任務2按照邏輯表達式規(guī)則判定(predicate),從不會進入條件變量等待隊列,只有3以上的任務才會進入條件變量隊列等待。
圖2 三任務輪流執(zhí)行邏輯規(guī)則
圖3 管程內活動進程退出管程前對伙伴組條件變量隊列的測試
從例1看到,C51帶有Test and Set Lock語句(由8051硬件功能確定),這是C51能夠簡單實現(xiàn)管程入口互斥鎖的首要條件,也即管程(模塊)實現(xiàn)的互斥機制部分(理論上編譯器負責的加鎖部分)。
從例2看到,RTX51Tiny具有wait/signal函數(shù),自身已能完成伙伴組間同步機制,因而能夠方便實現(xiàn)條件變量機制。這是管程(模塊)實現(xiàn)的同步機制部分。
例3 經典同步/互斥問題(生產者與消費者問題)
操作系統(tǒng)文化中有一些經典的進程同步問題被廣為討論和分析,它們是從各種類型的同步問題中歸納、抽象出來的一般性模型;每個新的同步/互斥機制的發(fā)明都希望通過這些經典問題來展示自己的精妙之處。生產者與消費者問題被普遍用來檢驗同步/互斥機制的正確性。
兩個任務:生產者/消費者任務通過共享一個公共的固定大小的緩沖區(qū)(例3中為8字節(jié)。)而關聯(lián)。生產者將信息(產品)存入緩沖區(qū)供消費;消費者從緩沖區(qū)取出信息(產品)以消費。如果生產速度大于消費,供大于求,緩沖區(qū)將很快填滿,生產者必須睡眠以停止生產,等待消費者從緩沖區(qū)中取走一些數(shù)據(jù)項消費掉后再喚醒生產者。同樣,如果消費速度大于生產,供不應求,緩沖區(qū)很快為空,消費者必須睡眠以等待生產者生產一些數(shù)據(jù)項后再喚醒消費者。例3源程序略——編者注。
編者注:本文為期刊縮略版,全文見本刊網(wǎng)站www.mesnet.com.cn。
[1] 程遠,勾勇華,龔志勇,等.RTX51Tiny中信號量操作的實現(xiàn)[J].單片機與嵌入式系統(tǒng)應用,2004(10).
[2] Qingli.嵌入式系統(tǒng)的實時概念[M].王安生,譯.北京:北京航空航天大學出版社,2004.
[3] Andrew S.Tanenbaum.現(xiàn)代操作系統(tǒng)[M].3版.陳向群,馬紅兵,等譯.北京:機械工業(yè)出版社,2009.