ecos是多線程系統,并發執行造成了一些新問題的産生:多線程協同工作、對臨界資源的競争、線程間通信、線程間同步等等。其實,所有的多任務系統都會遇 到類似問題,計算機專家們總結了很多抽象模型來應對,方法手段很多,各有特色,每種作業系統可能隻實作了某個子集。ecos核心的同步機制提供了許多同步 原語,包括:互斥、條件變量、信号量、信箱、事件标志和Spinlock等。
抽象出來的同步原語操作主要包括:建立、删除、等待(阻塞/逾時阻塞/非阻塞)、釋放、設定、廣播、查詢資料、查詢狀态。
雖然操作大同小異,但每種同步原語的含義和适用情況不同,下面詳細介紹各種原語的使用方法和注意事項。
==========
* 互斥體 *
互斥體用于實作線程對資源的安全共享。适用于線程間或線程與滞後中斷服務程式DSR通路同一臨界資源時的安全保護。
考慮下面的例子:
static volatile int counter = 0;
void test(void)
{
......
counter++;
}
假設在某個時候counter的值為16,此時有兩個同一優先級的線程A和B,它們都調用上面的test函數。線程A讀取counter的值,并
将其值加1,此時,counter為17。線程B也做同樣的操作,counter的值為18。但是如果線程A在讀取counter的值為16後,在将其值
加1之前排程器排程運作線程B,此時,線程B讀取的仍然是counter原來的值16,操作完成後counter變為17。這樣,counter的值隻增
加了1,而不是2,是以最後counter的值隻是17,而不是18。這足以說明該應用程式的運作是不可靠的。
使用互斥體就可以安全地操作counter全局變量:
static cyg_mutex_t lock;
cyg_mutex_lock(&lock);
cyg_mutex_unlock(&lock);
互斥體的使用可能會引起優先級倒置問題的出現。假設有三個不同優先級的線程A、B、C,優先級A>B>C。A和B由于等待事件而處于
阻塞狀态,C得以運作。線程C在進入臨界區時鎖定了一個互斥體。當線程A、B被喚醒時,線程A要等待同一個互斥體,但它線上程C離開臨界區并釋放互斥體之
前不得不等待。與此同時,線程B卻可以毫無問題地正常運作。由于線程C比線程B優先級低,它在B被阻塞前将沒有機會運作。這樣線程A就不能運作。其結果就
是高優先級的線程A由于優先級比它低的線程B的原因而無法運作,這就發生了優先級倒置。
解決優先級倒置問題普遍使用的技術是:優先級置頂協定(Priority Ceiling Protocol)和優先級繼承協定(Priority Inheritance Protocol)。
優先級置頂意味着占有互斥體的線程在運作時的優先級比任何其他可以擷取該互斥體的線程的優先級都要高。ecos元件包通常無法知道系統中各種線程的詳細資訊,是以無法對元件包内部使用的互斥體設定合适的置頂優先級。設定高了會影響排程操作。
優先級繼承将占有互斥體的線程優先級提升到所有正在等待該互斥體的線程優先級的最高值。當一個線程等待正被另一優先級較低的線程占有的互斥體時,
擁有該互斥體的線程優先級被提升到正在等待該互斥體的線程優先級,優先級繼承比優先級置頂效率高,不過增加了同步調用開銷,而且實作起來比優先級置頂複
雜。
初始化 cyg_mutex_init
删除 cyg_mutex_destroy
鎖定 cyg_mutex_lock
嘗試鎖定 cyg_mutex_trylock
解鎖 cyg_mutex_unlock
釋放 cyg_mutex_release
設定置頂優先級 cyg_mutex_set_ceiling
設定協定 cyg_mutex_set_protocol
============
* 條件變量 *
條件變量是允許線程同時給多個線程發信号的一個同步機制。當線程等待一個條件變量時,它在進入等待狀态之前将釋放互斥體,在被喚醒後又重新擁有互斥體。這種操作是原子操作。
舉例說明見例1。
初始化 cyg_cond_init
删除 cyg_cond_destroy
等待 cyg_cond_wait
喚醒 cyg_cond_signal
廣播 cyg_cond_broadcast
帶逾時等待 cyg_cond_timed_wait
* 信号量 *
信号量是一個允許線程等待直到事件發生的同步原語。每個信号量都有一個整數計數器,如果計數器為0,那麼等待該信号量的線程将被阻塞。如果計數器
大于0,那麼等待的線程将消耗一個事件,即計數器減1。喚醒信号量将對計數器加1。即使事件連續快速發生多次,信号量也不會丢失資訊。
信号量的另一個用途是對資源的管理。計數器的值與目前可用資源的數目相對應。實際上,條件變量更适合于這種操作。
初始化 cyg_semaphore_init
删除 cyg_semaphore_destroy
等待 cyg_semaphore_wait
帶逾時等待 cyg_semaphore_timed_wait
非阻塞等待 cyg_semaphore_trywait
喚醒 cyg_semaphore_post
擷取資訊 cyg_semaphore_peek
========
* 信箱 *
信箱是一個類似于信号量的同步原語,還可以在事件發生時傳遞一些資料。有些系統稱之為消息隊列。被稱為消息的資料通常是資料結構的指針。信箱隻具有有限的容量,預設配置為10個槽位,有可能溢出。是以,信箱通常不能被DSR用來喚醒線程。
建立 cyg_mbox_create
删除 cyg_mbox_delete
獲得消息 cyg_mbox_get
帶逾時獲得消息 cyg_mbox_timed_get
非阻塞獲得消息 cyg_mbox_tryget
非删除獲得消息 cyg_mbox_peek_item
發送消息 cyg_mbox_put
帶逾時發送消息 cyg_mbox_timed_put
非阻塞發送消息 cyg_mbox_tryput
讀取消息數 cyg_mbox_peek
判斷是否有消息 cyg_mbox_waiting_to_get
發新消息前判斷 cyg_mbox_waiting_to_put
* 事件标志 *
事件标志允許線程等待一個或幾個不同類型的事件發生。還可以用于等待某些事件組合的發生。事件标志不存在溢出問題。
事件标志可以指定函數調用者阻塞(1)直到所有指定事件發生為止;(2)直到至少一個指定事件發生為止;(3)直到所有指定事件發生為止并清除事件标志;(2)直到至少一個指定事件發生為止并清除事件标志。
初始化 cyg_flag_init
删除 cyg_flag_destroy
設定标志位 cyg_flag_setbits
清除标志位 cyg_flag_maskbits
等待事件發生 cyg_flag_wait
逾時等待事件 cyg_flag_timed_wait
探查事件是否發生 cyg_flag_poll
傳回事件标志目前值 cyg_flag_peek
報告是否有線程等待 cyg_flag_waiting
* Spinlock *
Spinlock是為SMP系統中的應用線程提供的一個同步原語。Spinlock運作級别要低于其他同步原語(如互斥體)。特别在對中斷進行處
理以及線程需要共享硬體資源的情況下需要使用Spinlock。在SMP系統中,核心自身的實作也需要使用Spinlock。
必須強調的是,對Spinlock的擁有時間必須很短,一般為幾十條指令。在單處理器系統中,不應該使用Spinlock。
初始化 cyg_spinlock_init
删除 cyg_spinlock_destroy
聲稱 cyg_spinlock_spin
釋放 cyg_spinlock_clear
非阻塞聲稱 cyg_spinlock_try
檢查是否有等待 cyg_spinlock_test
安全聲稱 cyg_spinlock_spin_intsave
安全釋放 cyg_spinlock_clear_intsave
一個奔跑的程式員