从零入门 FreeRTOS操作系统之信号量
從零入門(mén) FreeRTOS操作系統(tǒng)之信號(hào)量
1 信號(hào)量的基本概念
信號(hào)量 (Semaphore) 是一種實(shí)現(xiàn)任務(wù)間通信的機(jī)制,可以實(shí)現(xiàn)任務(wù)之間同步或臨界資源的互斥訪問(wèn),常用于協(xié)助一組相互競(jìng)爭(zhēng)的任務(wù)來(lái)訪問(wèn)臨界資源。
在多任務(wù)系統(tǒng)中,各任務(wù)之間需要同步或互斥實(shí)現(xiàn)臨界資源的保護(hù),信號(hào)量功能可以為用戶提供這方面的支持。
抽象的來(lái)講,信號(hào)量是一個(gè)非負(fù)整數(shù),所有獲取它的任務(wù)都會(huì)將該整數(shù)減 1,當(dāng)該整數(shù)值為零時(shí),所有試圖獲取它的任務(wù)都將處于阻塞狀態(tài)。
通常一個(gè)信號(hào)量的計(jì)數(shù)值用于對(duì)應(yīng)有效的資源數(shù),表示剩下的可被占用的互斥資源數(shù)。其值的含義分兩種情況:
- 0:表示沒(méi)有積累下來(lái)的釋放信號(hào)量操作,且有可能有在此信號(hào)量上阻塞的任務(wù)。
- 正值:表示有一個(gè)或多個(gè)釋放信號(hào)量操作。
2 二值信號(hào)量
二值信號(hào)量既可以用于臨界資源訪問(wèn)也可以用于同步功能。
二值信號(hào)量和互斥信號(hào)量(以下使用互斥量表示互斥信號(hào)量)非常相似,但是有一些細(xì)微差別:互斥量有優(yōu)先級(jí)繼承機(jī)制,二值信號(hào)量則沒(méi)有這個(gè)機(jī)制。這使得二值信號(hào)量更偏向應(yīng)用于同步功能(任務(wù)與任務(wù)間的同步或任務(wù)和中斷間同步),而互斥量更偏向應(yīng)用于臨界資源的訪問(wèn)。
用作同步時(shí),信號(hào)量在創(chuàng)建后應(yīng)被置為空,任務(wù) 1 獲取信號(hào)量而進(jìn)入阻塞,任務(wù) 2 在某種條件發(fā)生后,釋放信號(hào)量,于是任務(wù) 1 獲得信號(hào)量得以進(jìn)入就緒態(tài),如果任務(wù) 1 的優(yōu)先級(jí)是最高的,那么就會(huì)立即切換任務(wù),從而達(dá)到了兩個(gè)任務(wù)間的同步。同樣的,在中斷服務(wù)函數(shù)中釋放信號(hào)量,任務(wù) 1 也會(huì)得到信號(hào)量,從而達(dá)到任務(wù)與中斷間的同步。
我們知道中斷要快進(jìn)快出,因此在裸機(jī)開(kāi)發(fā)中我們經(jīng)常是在中斷中做一個(gè)標(biāo)
記,然后在退出的時(shí)候進(jìn)行輪詢處理,這個(gè)就是類(lèi)似我們使用信號(hào)量進(jìn)行同步的,當(dāng)標(biāo)記發(fā)生了,我們?cè)僮銎渌虑椤T?FreeRTOS 中我們用信號(hào)量用于同步,任務(wù)與任務(wù)的同步、中斷與任務(wù)的同步,可以大大提高效率。
可以將二值信號(hào)量看作只有一個(gè)消息的隊(duì)列,因此這個(gè)隊(duì)列只能為空或滿(因此稱為二值),我們?cè)谶\(yùn)用的時(shí)候只需要知道隊(duì)列中是否有消息即可,而無(wú)需關(guān)注消息是什么。
3 計(jì)數(shù)信號(hào)量
二進(jìn)制信號(hào)量可以被認(rèn)為是長(zhǎng)度為 1 的隊(duì)列,而計(jì)數(shù)信號(hào)量則可以被認(rèn)為長(zhǎng)度大于 1 的隊(duì)列,信號(hào)量使用者依然不必關(guān)心存儲(chǔ)在隊(duì)列中的消息,只需關(guān)心隊(duì)列是否有消息即可。
顧名思義,計(jì)數(shù)信號(hào)量肯定是用于計(jì)數(shù)的,在實(shí)際的使用中,我們常將計(jì)數(shù)信號(hào)量用于事件計(jì)數(shù)與資源管理。每當(dāng)某個(gè)事件發(fā)生時(shí),任務(wù)或者中斷將釋放一個(gè)信號(hào)量(信號(hào)量計(jì)數(shù)值加 1),當(dāng)處理事件時(shí)(一般在任務(wù)中處理),處理任務(wù)時(shí)取走該信號(hào)量(信號(hào)量計(jì)數(shù)值減 1),信號(hào)量的計(jì)數(shù)值則表示還有多少個(gè)事件沒(méi)被處理。此外,系統(tǒng)還有很多資源,我們也可以使用計(jì)數(shù)信號(hào)量進(jìn)行資源管理,信號(hào)量的計(jì)數(shù)值表示系統(tǒng)中可用的資源數(shù)目,任務(wù)必須先獲取到信號(hào)量才能獲取資源訪問(wèn)權(quán),當(dāng)信號(hào)量的計(jì)數(shù)值為零時(shí)表示系統(tǒng)沒(méi)有可用的資源,但是要注意,在使用完資源的時(shí)候必須歸還信號(hào)量,否則當(dāng)計(jì)數(shù)值為 0 的時(shí)候任務(wù)就無(wú)法訪問(wèn)該資源了。
計(jì)數(shù)型信號(hào)量允許多個(gè)任務(wù)對(duì)其進(jìn)行操作,但限制了任務(wù)的數(shù)量。比如有一個(gè)停車(chē)場(chǎng),里面只有 100 個(gè)車(chē)位,那么能停的車(chē)只有 100 輛,也相當(dāng)于我們的信號(hào)量有 100 個(gè),假如一開(kāi)始停車(chē)場(chǎng)的車(chē)位還有 100 個(gè),那么每進(jìn)去一輛車(chē)就要消耗一個(gè)停車(chē)位,車(chē)位的數(shù)量就要減一,對(duì)應(yīng)的,我們的信號(hào)量在使用之后也需要減一,當(dāng)停車(chē)場(chǎng)停滿了100 輛車(chē)的時(shí)候,此時(shí)的停車(chē)位為 0,再來(lái)的車(chē)就不能停進(jìn)去了,否則將造成事故,也相當(dāng)于我們的信號(hào)量為 0,后面的任務(wù)對(duì)這個(gè)停車(chē)場(chǎng)資源的訪問(wèn)也無(wú)法進(jìn)行,當(dāng)有車(chē)從停車(chē)場(chǎng)離開(kāi)的時(shí)候,車(chē)位又空余出來(lái)了,那么,后面的車(chē)就能停進(jìn)去了,我們信號(hào)量的操作也是一樣的,當(dāng)我們釋放了這個(gè)資源,后面的任務(wù)才能對(duì)這個(gè)資源進(jìn)行訪問(wèn)。
4 互斥信號(hào)量
互斥信號(hào)量其實(shí)是特殊的二值信號(hào)量,由于其特有的優(yōu)先級(jí)繼承機(jī)制從而使它更適用于簡(jiǎn)單互鎖,也就是保護(hù)臨界資源。
用作互斥時(shí),信號(hào)量創(chuàng)建后可用信號(hào)量個(gè)數(shù)應(yīng)該是滿的,任務(wù)在需要使用臨界資源時(shí),(臨界資源是指任何時(shí)刻只能被一個(gè)任務(wù)訪問(wèn)的資源),先獲取互斥信號(hào)量,使其變空,這樣其他任務(wù)需要使用臨界資源時(shí)就會(huì)因?yàn)闊o(wú)法獲取信號(hào)量而進(jìn)入阻塞,從而保證了臨界資源的安全。
在操作系統(tǒng)中,我們使用信號(hào)量的很多時(shí)候是為了給臨界資源建立一個(gè)標(biāo)志,信號(hào)量表示了該臨界資源被占用情況。這樣,當(dāng)一個(gè)任務(wù)在訪問(wèn)臨界資源的時(shí)候,就會(huì)先對(duì)這個(gè)資源信息進(jìn)行查詢,從而在了解資源被占用的情況之后,再做處理,從而使得臨界資源得到有效的保護(hù)。
5 遞歸信號(hào)量
遞歸信號(hào)量,見(jiàn)文知義,遞歸嘛,就是可以重復(fù)獲取調(diào)用的,本來(lái)按照信號(hào)量的特性,每獲取一次可用信號(hào)量個(gè)數(shù)就會(huì)減少一個(gè),但是遞歸則不然,對(duì)于已經(jīng)獲取遞歸互斥量的任務(wù)可以重復(fù)獲取該遞歸互斥量,該任務(wù)擁有遞歸信號(hào)量的所有權(quán)。任務(wù)成功獲取幾次遞歸互斥量,就要返還幾次,在此之前遞歸互斥量都處于無(wú)效狀態(tài),其他任務(wù)無(wú)法獲取,只有持有遞歸信號(hào)量的任務(wù)才能獲取與釋放。
6 二值信號(hào)量運(yùn)作機(jī)制
創(chuàng)建信號(hào)量時(shí),系統(tǒng)會(huì)為創(chuàng)建的信號(hào)量對(duì)象分配內(nèi)存,并把可用信號(hào)量初始化為用戶自定義的個(gè)數(shù), 二值信號(hào)量的最大可用信號(hào)量個(gè)數(shù)為 1。
任何任務(wù)都可以從創(chuàng)建的二值信號(hào)量資源中獲取一個(gè)二值信號(hào)量,獲取成功則返回正確,否則任務(wù)會(huì)根據(jù)用戶指定的阻塞超時(shí)時(shí)間來(lái)等待其它任務(wù)/中斷釋放信號(hào)量。在等待這段時(shí)間,系統(tǒng)將任務(wù)變成阻塞態(tài),任務(wù)將被掛到該信號(hào)量的阻塞等待列表中。
在二值信號(hào)量無(wú)效的時(shí)候,假如此時(shí)有任務(wù)獲取該信號(hào)量的話,那么任務(wù)將進(jìn)入阻塞狀態(tài)。
假如某個(gè)時(shí)間中斷/任務(wù)釋放了信號(hào)量,其過(guò)程如下圖所示。
由于獲取無(wú)效信號(hào)量而進(jìn)入阻塞態(tài)的任務(wù)將獲得信號(hào)量并且恢復(fù)為就緒態(tài),二值信號(hào)量運(yùn)作機(jī)制如下圖所示。
7 計(jì)數(shù)信號(hào)量運(yùn)作機(jī)制
計(jì)數(shù)信號(hào)量可以用于資源管理,允許多個(gè)任務(wù)獲取信號(hào)量訪問(wèn)共享資源,但會(huì)限制任務(wù)的最大數(shù)目。訪問(wèn)的任務(wù)數(shù)達(dá)到可支持的最大數(shù)目時(shí),會(huì)阻塞其他試圖獲取該信號(hào)量的任務(wù),直到有任務(wù)釋放了信號(hào)量。這就是計(jì)數(shù)型信號(hào)量的運(yùn)作機(jī)制,雖然計(jì)數(shù)信號(hào)量允許多個(gè)任務(wù)訪問(wèn)同一個(gè)資源,但是也有限定,比如某個(gè)資源限定只能有3 個(gè)任務(wù)訪問(wèn),那么第4 個(gè)任務(wù)訪問(wèn)的時(shí)候,會(huì)因?yàn)楂@取不到信號(hào)量而進(jìn)入阻塞,等到有任務(wù)(比如任務(wù)1)釋放掉該資源的時(shí)候,第4 個(gè)任務(wù)才能獲取到信號(hào)量從而進(jìn)行資源的訪問(wèn),其運(yùn)作的機(jī)制如下圖所示。
8 信號(hào)量控制塊
信號(hào)量 API 函數(shù)實(shí)際上都是宏,它使用現(xiàn)有的隊(duì)列機(jī)制,這些宏定義在 semphr.h 文件中,如果使用信號(hào)量或者互斥量,需要包含 semphr.h 頭文件。所以 FreeRTOS 的信號(hào)量控制塊結(jié)構(gòu)體與消息隊(duì)列結(jié)構(gòu)體是一模一樣的,只不過(guò)結(jié)構(gòu)體中某些成員變量代表的含義不一樣而已。
typedef struct QueueDefinition {int8_t *pcHead;int8_t *pcTail;int8_t *pcWriteTo;union{int8_t *pcReadFrom;UBaseType_t uxRecursiveCallCount;} u;List_t xTasksWaitingToSend;List_t xTasksWaitingToReceive;volatile UBaseType_t uxMessagesWaiting;UBaseType_t uxLength;UBaseType_t uxItemSize;volatile int8_t cRxLock;volatile int8_t cTxLock;#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )uint8_t ucStaticallyAllocated;#endif#if ( configUSE_QUEUE_SETS == 1 )struct QueueDefinition *pxQueueSetContainer;#endif#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t uxQueueNumber;uint8_t ucQueueType;#endif} xQUEUE;typedef xQUEUE Queue_t;volatile UBaseType_t uxMessagesWaiting;:如果控制塊結(jié)構(gòu)體是用于消息隊(duì)列: uxMessagesWaiting 用來(lái)記錄當(dāng)前消息隊(duì)列的消息個(gè)數(shù);如果控制塊結(jié)構(gòu)體被用于信號(hào)量的時(shí)候,這個(gè)值就表示有效信號(hào)量個(gè)數(shù),有以下兩種情況:
-
如果信號(hào)量是二值信號(hào)量、互斥信號(hào)量,這個(gè)值是 1 則表示有可用信號(hào)量,如果是 0 則表示沒(méi)有可用信號(hào)量。
-
如果是計(jì)數(shù)信號(hào)量,這個(gè)值表示可用的信號(hào)量個(gè)數(shù),在創(chuàng)建計(jì)數(shù)信號(hào)量的時(shí)候會(huì)被初始化一個(gè)可用信號(hào)量個(gè)數(shù) uxInitialCount,最大不允許超過(guò)創(chuàng)建信號(hào)量的初始值 uxMaxCount。
UBaseType_t uxLength;:如果控制塊結(jié)構(gòu)體是用于消息隊(duì)列:uxLength 表示隊(duì)列的長(zhǎng)度,也就是能存放多少消息;如果控制塊結(jié)構(gòu)體被用于信號(hào)量的時(shí)候,·uxLength· 表示最大的信號(hào)量可用個(gè)數(shù),會(huì)有以下兩種情況:
- 如果信號(hào)量是二值信號(hào)量、互斥信號(hào)量,uxLength 最大為 1,因?yàn)樾盘?hào)量要么是有效的,要么是無(wú)效的。
- 如果是計(jì)數(shù)信號(hào)量,這個(gè)值表示最大的信號(hào)量個(gè)數(shù),在創(chuàng)建計(jì)數(shù)信號(hào)量的時(shí)候?qū)⒂捎脩糁付ㄟ@個(gè)值 uxMaxCount。
UBaseType_t uxItemSize;:如果控制塊結(jié)構(gòu)體是用于消息隊(duì)列:uxItemSize 表示單個(gè)消息的大小;如果控制塊結(jié)構(gòu)體被用于信號(hào)量的時(shí)候,則無(wú)需存儲(chǔ)空間,為 0 即可。
總結(jié)
以上是生活随笔為你收集整理的从零入门 FreeRTOS操作系统之信号量的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C语言之 scanf() 函数的用法
- 下一篇: java信息管理系统总结_java实现科