任務間通信機制是多任務間相互同步和通信以協調各自活動的主要手段。VxWorks提供的任務間通信手段按其速度由快到慢包括信號量、消息隊列、管道到網絡透明的套接字。
任務間通信機制:
共享內存,數據的簡單共享;
信號量,基本的互斥和同步;
消息隊列和管道,同一CPU內多任務間消息傳遞。
Sockets,遠程調用,任務間透明的網絡通信。
Signals,用於異常處理。
共享數據結構
任務共存於單一的線性地址空間。任一程序中定義的全局變量,都可以被所用任務直接訪問。爲了方便編程,自身定義幾種數據類型:線性緩衝、環形緩衝、連接鏈等。可以被運行在不同上下文的代碼引用。
連接鏈是一種雙向連接的數據結構,定義在\target\h\lstLib.h中。
環形緩衝定義在\target\h\rngLib.h中。環形緩衝在用於任務和中斷服務程序間傳送字符非常有用。環形緩衝大小固定,以先進先出方式工作。
需要考慮互斥問題。僅僅服務於一個讀任務和寫任務時,不需要用信號量來控制對環形緩衝的訪問。當讀取任務讀取下一個節點時,該操作將使該節點變爲空,使得節點對新數據可用。如果讀任務不能跟上寫任務的速度,緩衝區將會溢出,應用數據可能丟失。這種情形需要較大的環形緩衝。
互斥
當一個共享地址空間簡單地用於交換數據時,爲避免競爭,需要對該內存的訪問上鎖。互斥方法包括:禁止中斷,禁止搶佔,和使用信號量對資源上鎖。
中斷上鎖:intLock();intUnlock(lock);之間是不能被中斷的臨界區。
搶佔上鎖:taskLock();taskUnlock();不能被中斷的臨界區;
中斷上鎖時間與控制搶佔禁止時間儘可能短;
一種更好的機制是信號量。
vxworks信號量是提供任務間通信、同步和互斥的最優選擇,它提供任務間的最快通信,也是提供任務間同步和互斥的主要手段。
對於互斥,信號量可以上鎖對共享資源的訪問,並且比禁止中斷或禁止搶佔提供更精確的互斥粒度。
三種類型的信號量:
a二進制:最快的最常用的信號量,可用於同步或互斥;
b互斥:爲解決具有內在的互斥問題、優先級繼承、刪除安全和遞歸等情況而最優化的特殊的二進制信號量;
c計數器:類似於二進制信號量,但是隨信號量釋放的次數改變而改變。適合於一個資源的多個實例需要保護的情形。
不僅提供主要爲vxworks設計的wind信號量,同時爲了使應用程序具有可移植性,也提供POSIX信號量。
信號量控制:wind信號量提供一套單一的接口用於控制信號量,類型僅僅由創建函數確定,其他接口根據需要處理的信號量類型自動完成相應的操作。
a對一個需要互斥訪問資源的操作由semTake()和semGive()對括起。
信號量用於互斥和同步情況下不同的狀態順序:
1、用於互斥時,信號量最初是滿的、可用的,每個任務首先是取,然後放回;
2、用於同步時,信號量最初是空的、不可用的,一個任務首先是等待由其他任務釋放的信號量。
b互斥信號量
是一種特殊的二進制信號量,主要解決具有內在的互斥問題:優先級繼承、刪除安全和對資源的遞歸訪問等。
基本行爲與二進制信號量一致,不同之處:
僅用於互斥;
僅能由取(semTake())它的任務釋放;
不能在ISR中釋放(semGive());
semFlush()操作非法。
1、優先級倒置
互斥信號量有個選項:SEM_INVERSION_SAFE使用這個選項將使能優先級繼承算法。必須與優先級隊列SEM_Q_PRIORITY一齊使用。semId=semCreate(SEM_Q_PRIORITY|SEM_INVERSION_SAFE);
優先級繼承協議確保擁有資源的任務以阻塞在該資源上的所有任務中優先級最高的任務有限執行。一旦這個任務的優先級被擡高,它將以這個高優先級運行,直至它持有的所有互斥信號量全部釋放。然後,該任務返回正常狀態。
2、刪除安全:
在一個受信號量保護的臨界區,經常需要保護在臨界區執行的任務不會被以外地刪除。刪除一個在臨界區執行的任務可能引起保護資源的信號量不可用,可能導致資源處於破壞狀態,也就導致了其他要訪問該資源的所有任務無法滿足。
原語taskSafe()和taskUnsafe()提供瞭解決任務避免被意外刪除的一種方法。同時,互斥信號量提供了選項SEM_DELETE_SAFE,該選項,每次調用semTake()時隱含使能taskSafe(),每次調用semGive()時隱含使能taskUnsafe().semId=semCreate(SEM_FIFO|SEM_DELETE_SAFE);
3、遞歸資源訪問
在被釋放之前,一個被遞歸獲取N次的互斥信號量必須被釋放與獲取相同的n次。這由系統中的一個計數器來跟蹤,每次semTake()調用計數器將加一,每次semGive()調用計數器將減一。
c計數器洗好了
除了像二進制信號量那樣工作外,還保持對信號量釋放次數的跟蹤。信號量每次釋放,計數器加一,每次獲取,計數器減一,當計數器減到0,試圖獲取該信號量的任務被阻塞。
wind信號量提供在阻塞狀態判斷超時的能力,由semTake()特定的參數控制。參數是一定量的系統ticks數目,表示任務將要在阻塞態等待的時間。
如果任務在規定時間內成功地取得了信號量,semTake()返回OK,當規定的超時值已過,semTake()未能成功取得信號量而返回ERROR,系統設置errno的值。
NO_WAIT(=0)不要等待,如果調用的信號量不可用,將直接返回,系統將errno設置爲S_objLib_OBJ_UNAVAILABLE。
一個以正整數n作爲參數的semTake()調用,如果等待n個tick後,請求的信號量仍不可用,系統將errno設置爲S_objLib_OBJ_TIMEOUT,調用返回。
WAIT_FOREVER(=-1)無限期等待,一直等到請求的信號量可用。
隊列
先進先出FIFO
創建時確定隊列類型,使用優先級繼承的信號量必須選擇使用基於優先級的隊列。
c、計數器信號量
二進制信號量可以用於任務間同步,但是如果事件發生的足夠快,可能導致數據丟失。即如果事件產生的速度超過任務處理該事件的速度,就可能會發生數據丟失。使用計數器信號量可以解決這一問題。
消息隊列
信號量提供高速的任務間同步和互斥機制,但常常需要一種較高級的允許合作任務之間相互通信的機制。VxWorks中,單CPU任務間主要的通信機制是消息隊列。允許長度可變、數目可變的消息排隊。任何任務或ISR可以發送消息到消息隊列。任何任務可以從消息隊列中接收消息。兩個任務間全雙工通信一般需要兩個消息隊列,每一個提供一個流通方向。
VxWorks提供兩個消息隊列函數庫:
一個是msgQLib,提供Wind消息隊列,專門爲vxworks設計;
另一個是mqPxLib,提供與POSIX實時擴展標準(1003.1b)兼容。
1、Wind消息隊列
該庫按照FIFO排隊的消息隊列。但有一個例外,Wind消息隊列有兩個優先級,高優先級的消息將放在隊列的頭部。
消息隊列由msgQCreate()創建,以它能夠排隊的最大的消息數目以及每個消息的最大字節長度作爲參數。預先分配足夠的緩衝空間。
超時
msgQReceive()和msgQSend()均可有超時作爲參數。NO_WAIT(=0)意味着立即返回;WAIT_FOREVER(=-1)意味着程序永不超時。
緊急消息
msgQSend()使用一個參數來指定消息的優先級,正常MSG_PRI_NORMAL或緊急MSG_PRI_URGENT.正常優先級消息追加到消息隊列的尾部,緊急優先級任務添加到消息隊列的首部。
二、POSIX消息隊列
初始化函數mqPxLibInit(),使得POSIX消息隊列函數可用,在使用其他消息隊列函數之前,系統初始化代碼必須調用這個函數。在任務集合使用一個消息隊列通信之前,其中之一必須以O_CREAT作爲參數調用mq_open()創建這個消息隊列,一旦消息隊列已經創建,其他的任務可以根據其名字打開這個消息隊列,然後可以收發消息。僅僅需要第一個打開這個隊列的任務使用O_CREAT參數,後續的發送任務使用O_WRONLY參數打開該隊列,接收任務使用O_RDONLY參數打開該隊列,既收又發的任務使用O_RDWR作爲打開函數的參數。
mq_send()將消息發送到消息隊列。如果消息隊列已滿,如果一個任務試圖向該隊列發送消息,這個任務將被阻塞,直到另外的任務從這個隊列中讀走一個消息,使得空間可用爲止。爲避免該函數阻塞,可以使用O_NONBLOCK作爲參數打開消息隊列。消息隊列
滿,返回-1,並設置errno爲EAGAIN,而任務不會阻塞,允許程序再試一次或採取其他合適的操作。
mq_receice()同上。
mq_send()一個參數用來指定優先級,從0(最低)~31(最高)。
mq_close()關閉隊列,但不會摧毀,僅僅是保證調用任務不再使用它。
mq_unlink()不會立即摧毀隊列,阻止以後的任務打開該隊列,並將該隊列名字從名字表中移出,當最後關閉了該隊列,隊列就摧毀了。
mq_notify()要求系統當有一個消息進入一個空的消息隊列時通知它,這樣可以避免任務阻塞或輪詢等待一個消息。
該函數以消息進入空隊列時系統發給任務的信號(signal)作爲參數。
mq_notify()機制只對當前狀態爲空的消息隊列有效,當該隊列有新的消息可用時,提醒任務。如果消息隊列已有可用消息,當有更多消息到達時,通知不會發生。如果有其他任務因調用mq_receive()阻塞在該隊列上,也不會通知這個使用mq_notify()註冊的任務。
每個隊列任意時刻只能有一個註冊等待通知的任務,一旦隊列已經有一個任務等待通知,不會接受其他任務調用mq_notify()的註冊,直到這個通知請求已經滿足或者取消。
用NULL代替通知信號調用mq_notify()函數,刪除一個請求通知,僅僅當前註冊的任務才能刪除它的通知信號。
POSIX和Wind消息隊列比較
特徵 Wind消息 POSIX消息隊列(可移植性)
消息優先級 1 32
阻塞的任務隊列 FIFO或基於優先級 基於優先級
不帶超時的接收 可選 不可用
任務通知 Not available 可選(一個任務)
關閉/解鏈語法 無 有
管道
管道使用VxWorks I/O系統,可提供能與消息隊列互換的功能。
是一種由pipeDrv驅動程序管理的虛擬I/O設備。函數pipeDevCreate()創建一個管道設備以及與該管道相聯繫的底層消息隊列。
調用指定了要創建管道的名字、能夠排隊的消息的最大數目、每個消息的最大長度等信息。
status=pipeDevCreate("/pipe/name",max_msgs,max_length);
任務能夠使用標準I/O程序來打開、讀、寫管道,也可以調用ioctl函數設置控制屬性。當從一個沒有數據可用的空管道讀數據時,任務會堵塞。像對消息隊列的操作一樣,ISRs能夠向管道寫,但不能讀,像I/O設備。
管道能提供消息隊列不能提供的一個重要特徵,可以使用select()函數。它允許任務等待一個I/O設備集合之一的數據可用。select()函數也能夠與其他的異步I/O設備一起工作,包括網絡套接字和串行設備。因此,使用select()函數,任務能夠同時等待幾個管道、套接字和串行設備集合上的數據。
vxworks應用中的管道是一個先進/先出的緩衝。
一個任務可以創建一個管道,一旦管道創建,任務可以直接調用read()和write()語句對之進行讀寫訪問。如果任務試圖向一個已滿的管道執行寫操作,該任務將等待(掛起)。
管道強調文件描述符,消息隊列缺乏這種能力。可以提供select和其他基本I/O操作。