freertos(第七課,semaphore, mutex, msg queue)

freertos是一個多進程操作系統。
多進程的一個重要控制,就是進程同步。
大多數的OS,都會基於PV操作完成進程同步。
基於EVENT的進程同步,
event在OS中,被表示爲一個結構體對象,最簡單的情況下,只有一個數據成員。
需要同步的進程,分別向OS申請P操作或者申請V操作,並告知OS,所錨定的event對象。
OS檢查event的狀態,並對發出申請的進程執行相應的狀態切換。
申請P操作的進程,將被OS嘗試阻塞,如果event狀態允許進程繼續執行,則不阻塞,而允許進程繼續執行。如果event狀態不允許進程繼續執行,則進程被OS阻塞。
申請V操作的進程,將使OS嘗試喚醒被阻塞到event上的進程。
可以看出,申請P操作的進程,會對自身的狀態產生影響。申請V操作的進程,則會對其他的進程的狀態產生影響。
在協作模型中,下游總是需要等待上游完成,所以,下游等待,上游喚醒。
在生產者消費者協作模型中,消費者總是需要等待生產者完成。所以,消費者等待,生產者和喚醒。

互斥是進程同步的一個特例。
在這個協作模型中,進程自己首先是消費者,然後自己又是生產者。
由於令牌模型更符合互斥的場景,所以通常用令牌模型來描述互斥。
進程首先要獲得一個令牌,用完之後,再將令牌歸還。其他進程如果沒有獲得令牌,則需要等待。當令牌被一個進程歸還之後,OS會喚醒其他正在等待令牌的進程。

ISR和TASK的同步,是常見的場景。
ISR生產event,而TASK則消費event。

freertos提供了一系列的sem API。

SemaphoreHandle_t xSemaphoreCreateBinary(void);
SemaphoreHandle_t 
xSemaphoreCreateCounting(
		UBaseType_t uxMaxCount, 
		UBaseType_t uxInitialCount
		);

UBaseType_t uxSemaphoreGetCount(xSemaphore);


BaseType_t xSemaphoreGive(xSemaphore);
BaseType_t 
xSemaphoreGiveFromISR(
		xSemaphore, 
		BaseType_t* pxHigherPriorityTaskWoken
		);

BaseType_t xSemaphoreTake(xSemaphore, TickType_t xBlockTime);
BaseType_t
xSemaphoreTakeFromISR(
		xSemaphore, 
		BaseType_t* pxHigherPriorityTaskWoken
		);

二值信號量,一般用於進程同步,或者ISR和TASK的同步。
計數信號量,一般用於共享資源的緩衝區統計。
互斥量,一般用於共享資源的獨立訪問。

信號量在RTOS中,會引起優先級反轉問題。
當低優先級任務獲取信號量後,如果高優先級任務也需要這個信號量,則需要被阻塞,最終,實際上高優先級任務被降低到更低的級別了。
解決辦法就是互斥量。
當任務獲取一個互斥量時,它的優先級會被OS暫時提高到和所有等待該互斥量的任務中的最高優先級。從而保證了,任務在互斥訪問時,不會被搶佔。
當任務歸還互斥量時,優先級會被OS恢復到之前的級別。
這被稱爲優先級繼承。
優先級繼承不能解決優先級反轉,但是能夠將危害降到最低。

freertos提供了一系列的API。

SemaphoreHandle_t xSemaphoreCreateMutex(void);
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);

BaseType_t xSemaphoreTake(xSemaphore, TickType_t xBlockTime);
BaseType_t xSemaphoreGive(xSemaphore);

BaseType_t xSemaphoreTakeRecursive(xSemaphore, TickType_t xBlockTime);
BaseType_t xSemaphoreGiveRecursive(xSemaphore);

信號量和互斥量,只能用於進程間的同步,並不能傳遞更多的數據。
freertos,提供了msgqueue,用來在實現進程同步的同時,傳遞數據。

進程間通信,或者ISR和TASK之間通信,如果沒有msgqueue,則只能通過全局變量來進行,但是這有個問題,就是資源管理的問題。
freertos,提供了msgqueue來替代全局變量傳遞數據。
當消息被填充到msgqueue中時,是複製了一個副本的,這樣做的代價是,數據複製的CPU時間消耗,但是帶來的好處是,數據存放在專屬位置,與原始緩衝區無關。如果向將原始緩衝區傳遞給其他進程,則只需要將緩衝區的指針發送到消息隊列中即可。隊列中只需要存放指針副本,節省了空間。
msgqueue是OS的公共資源,它不屬於某個特定的進程,任何進程都可以向隊列發送消息,或者提取消息。

以下,將消息隊列簡稱爲隊列,freertos中,也是簡稱爲Queue的。
進程向OS申請隊列服務時,實際上也同時向OS申請了POV操作(P-Operating-V)。
即,首先是OS判斷是否執行P操作,如果進程沒有被阻塞,纔會繼續執行數據複製的操作。在數據複製完成後,OS會執行V操作,將阻塞的其他進程喚醒。
在發送方向上,
當進程向OS申請發送消息時,OS首先會嘗試阻塞進程,如果隊列緩衝區有空閒位置,則不阻塞進程,允許進程繼續執行後續的數據複製操作。在執行完後,OS會喚醒WAITFORRECEIVELIST中的一個進程,讓它能夠取走隊列中的消息。
如果隊列緩衝區滿,則阻塞進程,將進程插入WAITFORSENDLIST中。
在接收方向上,
當進程向OS申請接收消息時,OS首先會嘗試阻塞進程,如果隊列緩衝區中有消息,則不阻塞進程,允許進程繼續執行後續的數據複製操作。在執行完成後,OS會喚醒WAITFORSENDLIST中的一個進程,讓它能夠填充隊列中的空閒位置。
如果隊列緩衝區空,則阻塞進程,將進程插入WAITFORRECEIVELIST中。

freertos提供了一系列API。

QueueHandle_t 
xQueueCreate(
		UBaseType_t uxQueueLength,
		UBaseType_t uxItemSize
		);
void vQueueDelete(QueueHandle_t xQueue);


BaseType_t 
xQueueSend(
		QueueHandle_t xQueue,
		const void* pvItemToQueue,
		TickType_t xTicksToWait
		);

BaseType_t 
xQueueSendToFront(
		QueueHandle_t xQueue,
		const void* pvItemToQueue,
		TickType_t xTicksToWait
		);

BaseType_t 
xQueueOverwrite(
		QueueHandle_t xQueue,
		const void* pvItemToQueue
		);
		
BaseType_t 
xQueueSendFromISR(
		QueueHandle_t xQueue,
		const void* pvItemToQueue,
		BaseType_t * pxHigherPriorityTaskWoken
		);
		
BaseType_t 
xQueueSendToFrontFromISR(
		QueueHandle_t xQueue,
		const void* pvItemToQueue,
		BaseType_t * pxHigherPriorityTaskWoken
		);
		
BaseType_t 
xQueueOverwriteFromISR(
		QueueHandle_t xQueue,
		const void* pvItemToQueue,
		BaseType_t * pxHigherPriorityTaskWoken,
		);

BaseType_t 
xQueueReceive(
		QueueHandle_t xQueue,
		void* pvBuffer,
		TickType_t xTicksToWait
		);

BaseType_t 
xQueuePeek(
		QueueHandle_t xQueue,
		void* pvBuffer,
		TickType_t xTicksToWait
		);

BaseType_t 
xQueueReceiveFromISR(
		QueueHandle_t xQueue,
		void* pvBuffer,
		BaseType_t* pxTaskWoken
		);
		
BaseType_t 
xQueuePeekFromISR(
		QueueHandle_t xQueue,
		void* pvBuffer,
		BaseType_t* pxTaskWoken
		);

隊列如果是FIFO方式,那麼就使用send,如果是LIFO方式,那麼就使用sendtofront。
通常在使用時,由多個進程或者ISR向同一個隊列中發送消息,而只有一個進程從隊列中接收消息,並依次處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章