1.1.信號(hào)量
在多任務(wù)操作系統(tǒng)中,不同的任務(wù)之間需要同步運(yùn)行,信號(hào)量功能可以為用戶提供這方面的支持。信號(hào)量(Semaphore)是一種實(shí)現(xiàn)任務(wù)間通信的機(jī)制,實(shí)現(xiàn)任務(wù)之間同步或臨界資源的互斥訪問。
1.2. 信號(hào)量的使用方式
信號(hào)量可以被任務(wù)獲取或者申請(qǐng),不同的信號(hào)量通過(guò)信號(hào)量索引號(hào)來(lái)唯一確定,每個(gè)信號(hào)量都有一個(gè)計(jì)數(shù)值和任務(wù)隊(duì)列。
通常一個(gè)信號(hào)量的計(jì)數(shù)值用于對(duì)應(yīng)有效的資源數(shù),表示剩下的可被占用的互斥資源數(shù),其值的含義分兩種情況:
0:表示沒有積累下來(lái)的 Post 操作,且有可能有在此信號(hào)量上阻塞的任務(wù);
正值:表示有一個(gè)或多個(gè) Post 下來(lái)的釋放操作;
當(dāng)任務(wù)申請(qǐng)(Pend)信號(hào)量時(shí),如果申請(qǐng)成功,則信號(hào)量的計(jì)數(shù)值遞減,如若申請(qǐng)失敗,則掛起在該信號(hào)量的等待任務(wù)隊(duì)列上,一旦有任務(wù)釋放該信號(hào)量,則等待任務(wù)隊(duì)列中的任務(wù)被喚醒開始執(zhí)行。
1.3. 信號(hào)量的使用場(chǎng)景
信號(hào)量是一種非常靈活的同步方式,可以運(yùn)用在多種場(chǎng)合中,實(shí)現(xiàn)鎖、同步、資源計(jì)數(shù)等功能,也能方便的用于任務(wù)與任務(wù),中斷與任務(wù)的同步中。
互斥鎖
用作互斥時(shí),信號(hào)量創(chuàng)建后記數(shù)是滿的,在需要使用臨界資源時(shí),先申請(qǐng)信號(hào)量,使其變空,這樣其他任務(wù)需要使用臨界資源時(shí)就會(huì)因?yàn)闊o(wú)法申請(qǐng)到信號(hào)量而阻塞,從而保證了臨界資源的安全。
任務(wù)間同步
用作同步時(shí),信號(hào)量在創(chuàng)建后被置為空,任務(wù)1申請(qǐng)信號(hào)量而阻塞,任務(wù)2在某種條件發(fā)生后,釋放信號(hào)量,于是任務(wù)1得以進(jìn)入 READY 或 RUNNING 態(tài),從而達(dá)到了兩個(gè)任務(wù)間的同步。
資源計(jì)數(shù)
用作資源計(jì)數(shù)時(shí),信號(hào)量的作用是一個(gè)特殊的計(jì)數(shù)器,可以遞增或者遞減,但是值永遠(yuǎn)不能為負(fù)值,典型的應(yīng)用場(chǎng)景是生產(chǎn)者與消費(fèi)者的場(chǎng)景。
中斷與任務(wù)的同步
用作中斷與任務(wù)的同步時(shí),可以在中斷未觸發(fā)時(shí)將信號(hào)量的值置為0,從而堵塞斷服務(wù)處理任務(wù),一旦中斷被觸發(fā),則喚醒堵塞的中斷服務(wù)處理任務(wù)進(jìn)行中斷處理。
2. 信號(hào)量API
Huawei LiteOS 系統(tǒng)中的信號(hào)量模塊為用戶提供創(chuàng)建/刪除信號(hào)量、申請(qǐng)/釋放信號(hào)量的功能。
Huawei LiteOS 系統(tǒng)中提供的信號(hào)量 API 都是以 LOS 開頭,但是這些 API 使用起來(lái)比較復(fù)雜,所以本文中我們使用 Huawei IoT Link SDK 提供的統(tǒng)一API接口進(jìn)行實(shí)驗(yàn),這些接口底層已經(jīng)使用 LiteOS 提供的API實(shí)現(xiàn),對(duì)用戶而言更為簡(jiǎn)潔,API列表如下:
osal的api接口聲明在
相關(guān)的接口定義在osal.c中,基于LiteOS的接口實(shí)現(xiàn)在 liteos_imp.c文件中:
osal_semp_create | 信號(hào)量創(chuàng)建 |
osal_semp_del | 信號(hào)量刪除 |
osal_semp_pend | 信號(hào)量申請(qǐng) |
osal_semp_post | 信號(hào)量釋放 |
接口名 | 功能描述 |
---|
2.1. osal_semp_create
osal_semp_create接口用于創(chuàng)建一個(gè)信號(hào)量,其接口原型如下:
bool_t??osal_semp_create(osal_semp_t?*semp,int?limit,int?initvalue) {????bool_t?ret?=?false;????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->semp_create)) ????{ ????????ret?=?s_os_cb->ops->semp_create(semp,limit,initvalue); ????}????return?ret; }
該接口的參數(shù)說(shuō)明如下表:
semp | 信號(hào)量索引ID的地址 |
limit | 信號(hào)量計(jì)數(shù)值的最大值 |
initvalue | 信號(hào)量計(jì)數(shù)值的初始值 |
返回值 | false - 創(chuàng)建失敗 |
返回值 | true - 創(chuàng)建成功 |
參數(shù) | 描述 |
---|
2.2. osal_semp_del
osal_semp_del接口用于刪除一個(gè)信號(hào)量,其接口原型如下:
bool_t??osal_semp_del(osal_semp_t?semp) {????bool_t?ret?=?false;????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->semp_del)) ????{ ????????ret?=?s_os_cb->ops->semp_del(semp); ????}????return?ret; }
該接口的參數(shù)說(shuō)明如下表:
semp | 信號(hào)量索引ID |
返回值 | false - 刪除失敗 |
返回值 | true - 刪除成功 |
參數(shù) | 描述 |
---|
2.3. osal_semp_pend
osal_semp_pend接口用于申請(qǐng)一個(gè)信號(hào)量,其接口原型如下:
bool_t??osal_semp_pend(osal_semp_t?semp,int?timeout) {????bool_t?ret?=?false;????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->semp_pend)) ????{ ????????ret?=?s_os_cb->ops->semp_pend(semp,timeout); ????}????return?ret; }
該接口的參數(shù)說(shuō)明如下表:
semp | 信號(hào)量索引ID |
timeout | 32位值,具體見下文 |
返回值 | false - 申請(qǐng)失敗 |
返回值 | true - 申請(qǐng)成功 |
參數(shù) | 描述 |
---|
信號(hào)量有三種申請(qǐng)模式:非阻塞模式、永久阻塞模式、定時(shí)阻塞模式,用?timeout?參數(shù)的值選擇。
非阻塞模式(0):
任務(wù)需要申請(qǐng)信號(hào)量,若當(dāng)前信號(hào)量的任務(wù)數(shù)沒有到信號(hào)量設(shè)定的上限,則申請(qǐng)成功。否則,立即返回申請(qǐng)失敗
永久阻塞模式(cn_osal_timeout_forever或0xFFFFFFFF):
任務(wù)需要申請(qǐng)信號(hào)量,若當(dāng)前信號(hào)量的任務(wù)數(shù)沒有到信號(hào)量設(shè)定的上限,則申請(qǐng)成功。否則,該任務(wù)進(jìn)入阻塞態(tài),系統(tǒng)切換到就緒任務(wù)中優(yōu)先級(jí)最高者繼續(xù)執(zhí)行。任務(wù)進(jìn)入阻塞態(tài)后,直到有其他任務(wù)釋放該信號(hào)量,阻塞任務(wù)才會(huì)重新得以執(zhí)行
定時(shí)阻塞模式(任意定時(shí)值,32bit):
任務(wù)需要申請(qǐng)信號(hào)量,若當(dāng)前信號(hào)量的任務(wù)數(shù)沒有到信號(hào)量設(shè)定的上限,則申請(qǐng)成功。否則,該任務(wù)進(jìn)入阻塞態(tài),系統(tǒng)切換到就緒任務(wù)中優(yōu)先級(jí)最高者繼續(xù)執(zhí)行。任務(wù)進(jìn)入阻塞態(tài)后,指定時(shí)間超時(shí)前有其他任務(wù)釋放該信號(hào)量,或者用戶指定時(shí)間超時(shí)后,阻塞任務(wù)才會(huì)重新得以執(zhí)行
由于中斷不能被阻塞,因此在申請(qǐng)信號(hào)量時(shí),阻塞模式不能在中斷中使用。
2.4. osal_semp_post
osal_semp_post接口用于釋放一個(gè)信號(hào)量,如果有任務(wù)阻塞于該信號(hào)量,則喚醒該信號(hào)量阻塞隊(duì)列上的第一個(gè)任務(wù),該任務(wù)進(jìn)入就緒態(tài),并進(jìn)行調(diào)度。
其接口原型如下:
bool_t??osal_semp_post(osal_semp_t?semp) {????bool_t?ret?=?false;????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->semp_post)) ????{ ????????ret?=?s_os_cb->ops->semp_post(semp); ????}????return?ret; }
該接口的參數(shù)說(shuō)明如下表:
semp | 信號(hào)量索引ID |
返回值 | false - 釋放失敗 |
返回值 | true - 釋放成功 |
參數(shù) | 描述 |
---|
3. 動(dòng)手實(shí)驗(yàn) —— 使用信號(hào)量進(jìn)行任務(wù)間同步
實(shí)驗(yàn)內(nèi)容
本實(shí)驗(yàn)中將創(chuàng)建兩個(gè)任務(wù),一個(gè)低優(yōu)先級(jí)任務(wù)task1,一個(gè)高優(yōu)先級(jí)任務(wù)task2,兩個(gè)任務(wù)之間使用信號(hào)量同步運(yùn)行,在串口終端中觀察兩個(gè)任務(wù)的運(yùn)行情況。
實(shí)驗(yàn)代碼
首先打開上一篇使用的 HelloWorld 工程,基于此工程進(jìn)行實(shí)驗(yàn)。
在Demo文件夾右擊,新建文件夾osal_kernel_demo用于存放內(nèi)核的實(shí)驗(yàn)文件(如果已有請(qǐng)忽略這一步)。
接下來(lái)在此文件夾中新建一個(gè)實(shí)驗(yàn)文件?osal_semp_demo.c,開始編寫代碼:
/*?使用osal接口需要包含該頭文件?*/#include?
編寫完成之后,要將我們編寫的osal_semp_demo文件添加到makefile中,加入整個(gè)工程的編譯:
這里有個(gè)較為簡(jiǎn)單的方法,直接修改Demo文件夾下的user_demo.mk配置文件,添加如下代碼:
#example?for?osal_semp_demo ifeq?($(CONFIG_USER_DEMO),?"osal_semp_demo") user_demo_src??=?${wildcard?$(TOP_DIR)/targets/STM32L431_BearPi/Demos/osal_kernel_demo/osal_semp_demo.c} user_demo_defs?=?-D?CONFIG_OSAL_SEMP_DEMO_ENABLE=1 endif
添加位置如圖:
這段代碼的意思是:
如果 CONFIG_USER_DEMO 宏定義的值是osal_semp_demo,則將osal_semp_demo.c文件加入到makefile中進(jìn)行編譯。
那么,如何配置 CONFIG_USER_DEMO 宏定義呢?在工程根目錄下的.sdkconfig文件中的末尾即可配置:
因?yàn)槲覀冃薷牧薽k配置文件,所以點(diǎn)擊重新編譯按鈕進(jìn)行編譯,編譯完成后點(diǎn)擊下載按鈕燒錄程序。
編譯之后會(huì)有警告,提示user_task1_entry和user_task2_entry函數(shù)沒有返回值,這里使用了while(1),不會(huì)返回,所以不用管。
實(shí)驗(yàn)現(xiàn)象
程序燒錄之后,即可看到程序已經(jīng)開始運(yùn)行,在串口終端中可看到實(shí)驗(yàn)的輸出內(nèi)容:
linkmain:V1.2.1?AT?11:30:59?ON?Nov?28?2019?sync_semp?semp?create?success.WELCOME?TO?IOT_LINK?SHELLLiteOS:/>task2?is?waiting?for?a?semp...task?1?post?a?semp!task?2?access?a?semp!task2?is?waiting?for?a?semp...task?1?post?a?semp!task?2?access?a?semp!task2?is?waiting?for?a?semp...task?1?post?a?semp!task?2?access?a?semp!……
評(píng)論
查看更多