這篇文章主要介紹“TencentOS tiny互斥鎖的原理以及實(shí)例用法”,在日常操作中,相信很多人在TencentOS tiny互斥鎖的原理以及實(shí)例用法問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”TencentOS tiny互斥鎖的原理以及實(shí)例用法”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
成都創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括孟連網(wǎng)站建設(shè)、孟連網(wǎng)站制作、孟連網(wǎng)頁(yè)制作以及孟連網(wǎng)絡(luò)營(yíng)銷策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,孟連網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到孟連省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
互斥鎖又稱互斥互斥鎖,是一種特殊的信號(hào)量,它和信號(hào)量不同的是,它具有互斥鎖所有權(quán)、遞歸訪問(wèn)以及優(yōu)先級(jí)繼承
等特性,在操作系統(tǒng)中常用于對(duì)臨界資源的獨(dú)占式
處理。在任意時(shí)刻互斥鎖的狀態(tài)只有兩種,開(kāi)鎖或閉鎖
,當(dāng)互斥鎖被任務(wù)持有時(shí),該互斥鎖處于閉鎖狀態(tài),當(dāng)該任務(wù)釋放互斥鎖時(shí),該互斥鎖處于開(kāi)鎖狀態(tài)。
一個(gè)任務(wù)持有互斥鎖就表示它擁有互斥鎖的所有權(quán),只有該任務(wù)才能釋放互斥鎖,同時(shí)其他任務(wù)將不能持有該互斥鎖,這就是互斥鎖的所有權(quán)
特性。
當(dāng)持有互斥鎖的任務(wù)再次獲取互斥鎖時(shí)不會(huì)被掛起,而是能遞歸獲取,這就是互斥鎖的遞歸訪問(wèn)
特性。這個(gè)特性與一般的信號(hào)量有很大的不同,在信號(hào)量中,由于已經(jīng)不存在可用的信號(hào)量,任務(wù)遞歸獲取信號(hào)量時(shí)會(huì)發(fā)生掛起任務(wù)最終形成死鎖
。
互斥鎖還擁有優(yōu)先級(jí)繼承
機(jī)制,它可以將低
優(yōu)先級(jí)任務(wù)的優(yōu)先級(jí)臨時(shí)提升
到與獲取互斥鎖的高
優(yōu)先級(jí)任務(wù)的優(yōu)先級(jí)相同
,盡可能降低
優(yōu)先級(jí)翻轉(zhuǎn)的危害。
在實(shí)際應(yīng)用中,如果想要實(shí)現(xiàn)同步功能,可以使用信號(hào)量,雖然互斥鎖也可以用于任務(wù)與任務(wù)間的同步,但互斥鎖更多的是用于臨界資源的互斥訪問(wèn)。
使用互斥鎖對(duì)臨界資源保護(hù)時(shí),任務(wù)必須先獲取互斥鎖以獲得訪問(wèn)該資源的所有權(quán),當(dāng)任務(wù)使用資源后,必須釋放互斥鎖以便其他任務(wù)可以訪問(wèn)該資源(而使用信號(hào)量保護(hù)臨界資源時(shí)則可能發(fā)生優(yōu)先級(jí)翻轉(zhuǎn),且危害
是不可控的)。
簡(jiǎn)單來(lái)說(shuō)就是高優(yōu)先級(jí)任務(wù)在等待低優(yōu)先級(jí)任務(wù)執(zhí)行完畢,這已經(jīng)違背了操作系統(tǒng)的設(shè)計(jì)初衷(搶占式調(diào)度)。
為什么會(huì)發(fā)生優(yōu)先級(jí)翻轉(zhuǎn)?
當(dāng)系統(tǒng)中某個(gè)臨界資源受到一個(gè)互斥鎖保護(hù)時(shí),任務(wù)訪問(wèn)該資源時(shí)需要獲得互斥鎖,如果這個(gè)資源正在被一個(gè)低優(yōu)先級(jí)
任務(wù)使用,此時(shí)互斥鎖處于閉鎖狀態(tài);如果此時(shí)一個(gè)高優(yōu)先級(jí)
任務(wù)想要訪問(wèn)該資源,那么高優(yōu)先級(jí)任務(wù)會(huì)因?yàn)楂@取不到互斥鎖而進(jìn)入阻塞態(tài),此時(shí)就形成高優(yōu)先級(jí)任務(wù)在等待低優(yōu)先級(jí)任務(wù)運(yùn)行(等待它釋放互斥鎖)。
優(yōu)先級(jí)翻轉(zhuǎn)是會(huì)產(chǎn)生危害
的,在一開(kāi)始設(shè)計(jì)系統(tǒng)的時(shí)候,就已經(jīng)指定任務(wù)的優(yōu)先級(jí),越重要的任務(wù)優(yōu)先級(jí)越高,但是發(fā)生優(yōu)先級(jí)翻轉(zhuǎn)后,高優(yōu)先級(jí)任務(wù)在等待低優(yōu)先級(jí)任務(wù),這就有可能讓高優(yōu)先級(jí)任務(wù)得不到有效的處理,嚴(yán)重時(shí)可能導(dǎo)致系統(tǒng)崩潰。
優(yōu)先級(jí)翻轉(zhuǎn)的危害是不可控的,因?yàn)榈蛢?yōu)先級(jí)任務(wù)很可能會(huì)被系統(tǒng)中其他中間優(yōu)先級(jí)
任務(wù)(低優(yōu)先級(jí)與高優(yōu)先級(jí)任務(wù)之間的優(yōu)先級(jí)任務(wù))搶占,這就有可能導(dǎo)致高優(yōu)先級(jí)任務(wù)將等待所有中間優(yōu)先級(jí)任務(wù)運(yùn)行完畢的情況,這種情況對(duì)高優(yōu)先級(jí)任務(wù)來(lái)說(shuō)是不可接受的,也是違背了操作系統(tǒng)設(shè)計(jì)的原則。
在TencentOS tiny
中為了降低
優(yōu)先級(jí)翻轉(zhuǎn)產(chǎn)生的危害使用了優(yōu)先級(jí)繼承算法
:暫時(shí)提高占有某種臨界資源的低優(yōu)先級(jí)任務(wù)的優(yōu)先級(jí),使之與在所有等待該資源的任務(wù)中,優(yōu)先級(jí)最高的任務(wù)優(yōu)先級(jí)相等,而當(dāng)這個(gè)低優(yōu)先級(jí)任務(wù)執(zhí)行完畢釋放該資源時(shí),優(yōu)先級(jí)恢復(fù)初始設(shè)定值(此處可以看作是低優(yōu)先級(jí)任務(wù)臨時(shí)繼承了高優(yōu)先級(jí)任務(wù)的優(yōu)先級(jí))。因此,繼承優(yōu)先級(jí)的任務(wù)避免了系統(tǒng)資源被任何中間優(yōu)先級(jí)任務(wù)搶占。互斥鎖的優(yōu)先級(jí)繼承機(jī)制,它確保高優(yōu)先級(jí)任務(wù)進(jìn)入阻塞狀態(tài)的時(shí)間盡可能短,以及將已經(jīng)出現(xiàn)的“優(yōu)先級(jí)翻轉(zhuǎn)”危害降低到最小
,但不能消除
優(yōu)先級(jí)翻轉(zhuǎn)帶來(lái)的危害。
值得一提的是TencentOS tiny
在持有互斥鎖時(shí)還允許調(diào)用修改任務(wù)優(yōu)先級(jí)的API接口。
TencentOS tiny
通過(guò)互斥鎖控制塊操作互斥鎖,其數(shù)據(jù)類型為k_mutex_t
,互斥鎖控制塊由多個(gè)元素組成。
pend_obj
有點(diǎn)類似于面向?qū)ο蟮睦^承,繼承一些屬性,里面有描述內(nèi)核資源的類型(如互斥鎖、隊(duì)列、互斥量等,同時(shí)還有一個(gè)等待列表list
)。
pend_nesting
實(shí)際上是一個(gè)uint8_t
類型的變量,記錄互斥鎖被獲取的次數(shù),如果pend_nesting
為0則表示開(kāi)鎖狀態(tài),不為0則表示閉鎖狀態(tài),且它的值記錄了互斥量被獲取的次數(shù)(擁有遞歸訪問(wèn)特性)
*owner
是任務(wù)控制塊指針,記錄當(dāng)前互斥鎖被哪個(gè)任務(wù)持有。
owner_orig_prio
變量記錄了持有互斥鎖任務(wù)的優(yōu)先級(jí),因?yàn)橛锌赡馨l(fā)生優(yōu)先級(jí)繼承機(jī)制而臨時(shí)改變?nèi)蝿?wù)的優(yōu)先級(jí)。(擁有優(yōu)先級(jí)繼承機(jī)制)。
owner_list
是一個(gè)列表節(jié)點(diǎn),當(dāng)互斥鎖被任務(wù)獲取時(shí),該節(jié)點(diǎn)會(huì)被添加到任務(wù)控制塊的task->mutex_own_list
列表中,表示任務(wù)自己獲取到的互斥鎖有哪些。當(dāng)互斥鎖被完全
釋放時(shí)(pend_nesting等于0
),該節(jié)點(diǎn)將從任務(wù)控制塊的task->mutex_own_list
列表中移除。
prio_pending
變量比較有意思:在一般的操作系統(tǒng)中,任務(wù)在持有互斥鎖時(shí)不允許修改優(yōu)先級(jí),但在TencentOS tiny
中是允許的,就是因?yàn)檫@個(gè)變量,當(dāng)一個(gè)任務(wù)處于互斥鎖優(yōu)先級(jí)反轉(zhuǎn)的時(shí)候,我假設(shè)他因?yàn)閮?yōu)先級(jí)反轉(zhuǎn)優(yōu)先級(jí)得到了提升,此時(shí)有用戶企圖改變這個(gè)任務(wù)的優(yōu)先級(jí),但這個(gè)更改后的優(yōu)先級(jí)會(huì)使此任務(wù)違背其優(yōu)先級(jí)必須比所有等待他所持有的互斥鎖的任務(wù)還高的原則時(shí),此時(shí)需要將用戶的這次優(yōu)先級(jí)更改請(qǐng)求記錄到prio_pending
里,待其釋放其所持有的互斥鎖后再更改,相當(dāng)于將任務(wù)優(yōu)先級(jí)的更改延后了。
舉個(gè)例子:好比一個(gè)任務(wù)優(yōu)先級(jí)是10,且持有一把鎖,此時(shí)一個(gè)優(yōu)先級(jí)為6的任務(wù)嘗試獲取這把鎖,那么此任務(wù)優(yōu)先級(jí)會(huì)被提升為6,如果此時(shí)用戶試圖更改他的優(yōu)先級(jí)為7,那么不能立刻響應(yīng)這次請(qǐng)求,必須要等這把鎖放掉的時(shí)候才能做真正的優(yōu)先級(jí)修改
typedef struct k_mutex_st { pend_obj_t pend_obj; k_nesting_t pend_nesting; k_task_t *owner; k_prio_t owner_orig_prio; k_list_t owner_list; } k_mutex_t;
typedef struct k_task_st { #if TOS_CFG_MUTEX_EN > 0u k_list_t mutex_own_list; /**< 任務(wù)擁有的互斥量 */ k_prio_t prio_pending; /*< 在持有互斥鎖時(shí)修改任務(wù)優(yōu)先級(jí)將被記錄到這個(gè)變量中,在釋放持有的互斥鎖時(shí)再更改 */ #endif } k_task_t;
在tos_config.h
中,使能互斥鎖的宏定義是TOS_CFG_MUTEX_EN
#define TOS_CFG_MUTEX_EN 1u
系統(tǒng)中每個(gè)互斥鎖都有對(duì)應(yīng)的互斥鎖控制塊,互斥鎖控制塊中包含了互斥鎖的所有信息,比如它的等待列表、它的資源類型,以及它的互斥鎖值,那么可以想象一下,創(chuàng)建互斥鎖的本質(zhì)是不是就是對(duì)互斥鎖控制塊進(jìn)行初始化呢?很顯然就是這樣子的。因?yàn)樵诤罄m(xù)對(duì)互斥鎖的操作都是通過(guò)互斥鎖控制塊來(lái)操作的,如果控制塊沒(méi)有信息,那怎么能操作嘛~
創(chuàng)建互斥鎖函數(shù)是tos_mutex_create()
,傳入一個(gè)互斥鎖控制塊的指針*mutex
即可。
互斥鎖的創(chuàng)建實(shí)際上就是調(diào)用pend_object_init()
函數(shù)將互斥鎖控制塊中的mutex->pend_obj
成員變量進(jìn)行初始化,它的資源類型被標(biāo)識(shí)為PEND_TYPE_MUTEX
。然后將mutex->pend_nesting
成員變量設(shè)置為0
,表示互斥鎖處于開(kāi)鎖狀態(tài);將mutex->owner
成員變量設(shè)置為K_NULL
,表示此事并無(wú)任務(wù)持有互斥鎖;將mutex->owner_orig_prio
成員變量設(shè)置為默認(rèn)值K_TASK_PRIO_INVALID
,畢竟此事沒(méi)有任務(wù)持有互斥鎖,也無(wú)需記錄持有互斥鎖任務(wù)的優(yōu)先級(jí)。最終調(diào)用tos_list_init()
函數(shù)將互斥鎖的持有互斥鎖任務(wù)的列表節(jié)點(diǎn)owner_list
。
__API__ k_err_t tos_mutex_create(k_mutex_t *mutex) { TOS_PTR_SANITY_CHECK(mutex); pend_object_init(&mutex->pend_obj, PEND_TYPE_MUTEX); mutex->pend_nesting = (k_nesting_t)0u; mutex->owner = K_NULL; mutex->owner_orig_prio = K_TASK_PRIO_INVALID; tos_list_init(&mutex->owner_list); return K_ERR_NONE; }
互斥鎖銷毀函數(shù)是根據(jù)互斥鎖控制塊直接銷毀的,銷毀之后互斥鎖的所有信息都會(huì)被清除,而且不能再次使用這個(gè)互斥鎖,當(dāng)互斥鎖被銷毀時(shí),其等待列表中存在任務(wù),系統(tǒng)有必要將這些等待這些任務(wù)喚醒,并告知任務(wù)互斥鎖已經(jīng)被銷毀了PEND_STATE_DESTROY
。然后產(chǎn)生一次任務(wù)調(diào)度以切換到最高優(yōu)先級(jí)任務(wù)執(zhí)行。
TencentOS tiny
對(duì)互斥鎖銷毀的處理流程如下:
調(diào)用pend_is_nopending()
函數(shù)判斷一下是否有任務(wù)在等待互斥鎖
如果有則調(diào)用pend_wakeup_all()
函數(shù)將這些任務(wù)喚醒,并且告知等待任務(wù)互斥鎖已經(jīng)被銷毀了(即設(shè)置任務(wù)控制塊中的等待狀態(tài)成員變量pend_state
為PEND_STATE_DESTROY
)。
調(diào)用pend_object_deinit()
函數(shù)將互斥鎖控制塊中的內(nèi)容清除,最主要的是將控制塊中的資源類型設(shè)置為PEND_TYPE_NONE
,這樣子就無(wú)法使用這個(gè)互斥鎖了。
將mutex->pend_nesting
成員變量恢復(fù)為默認(rèn)值0
。
如果刪除的時(shí)候有任務(wù)持有互斥鎖,那么調(diào)用mutex_old_owner_release()
函數(shù)將互斥鎖釋放。
進(jìn)行任務(wù)調(diào)度knl_sched()
注意:如果互斥鎖控制塊的RAM是由編譯器靜態(tài)分配
的,所以即使是銷毀了互斥鎖,這個(gè)內(nèi)存也是沒(méi)辦法釋放的。當(dāng)然你也可以使用動(dòng)態(tài)內(nèi)存為互斥鎖控制塊分配內(nèi)存,只不過(guò)在銷毀后要將這個(gè)內(nèi)存釋放掉,避免內(nèi)存泄漏。
__API__ k_err_t tos_mutex_destroy(k_mutex_t *mutex) { TOS_CPU_CPSR_ALLOC(); TOS_PTR_SANITY_CHECK(mutex); #if TOS_CFG_OBJECT_VERIFY_EN > 0u if (!pend_object_verify(&mutex->pend_obj, PEND_TYPE_MUTEX)) { return K_ERR_OBJ_INVALID; } #endif TOS_CPU_INT_DISABLE(); if (!pend_is_nopending(&mutex->pend_obj)) { pend_wakeup_all(&mutex->pend_obj, PEND_STATE_DESTROY); } pend_object_deinit(&mutex->pend_obj); mutex->pend_nesting = (k_nesting_t)0u; if (mutex->owner) { mutex_old_owner_release(mutex); } TOS_CPU_INT_ENABLE(); knl_sched(); return K_ERR_NONE; }
tos_mutex_pend_timed()
函數(shù)用于獲取互斥鎖,互斥鎖就像是臨界資源的令牌,任務(wù)只有獲取到互斥鎖時(shí)才能訪問(wèn)臨界資源。當(dāng)且僅當(dāng)互斥鎖處于開(kāi)鎖的狀態(tài),任務(wù)才能獲取互斥鎖成功,當(dāng)任務(wù)持有了某個(gè)互斥鎖的時(shí)候,其它任務(wù)就無(wú)法獲取這個(gè)互斥鎖,需要等到持有互斥鎖的任務(wù)進(jìn)行釋放后,其他任務(wù)才能獲取成功,任務(wù)通過(guò)互斥鎖獲取函數(shù)來(lái)獲取互斥鎖的所有權(quán),任務(wù)對(duì)互斥鎖的所有權(quán)是獨(dú)占的,任意時(shí)刻互斥鎖只能被一個(gè)任務(wù)持有,如果互斥鎖處于開(kāi)鎖狀態(tài),那么獲取該互斥鎖的任務(wù)將成功獲得該互斥鎖,并擁有互斥鎖的使用權(quán);如果互斥鎖處于閉鎖狀態(tài),獲取該互斥鎖的任務(wù)將無(wú)法獲得互斥鎖,任務(wù)將可能被阻塞,也可能立即返回,阻塞時(shí)間timeout
由用戶指定,在指定時(shí)間還無(wú)法獲取到互斥鎖時(shí),將發(fā)送超時(shí),等待任務(wù)將自動(dòng)恢復(fù)為就緒態(tài)。在任務(wù)被阻塞之前,會(huì)進(jìn)行優(yōu)先級(jí)繼承,如果當(dāng)前任務(wù)優(yōu)先級(jí)比持有互斥鎖的任務(wù)優(yōu)先級(jí)高,那么將會(huì)臨時(shí)提升持有互斥鎖任務(wù)的優(yōu)先級(jí)。
TencentOS tiny
提供兩組API接口用于獲取互斥鎖,分別是tos_mutex_pend_timed()
和tos_mutex_pend()
,主要的差別是參數(shù)不同:可選阻塞與永久阻塞獲取互斥鎖,實(shí)際獲取的過(guò)程都是一樣的。獲取互斥鎖的過(guò)程如下:
首先檢測(cè)傳入的參數(shù)是否正確,此處不僅會(huì)檢查互斥鎖控制塊的信息,還會(huì)調(diào)用TOS_IN_IRQ_CHECK()
檢查上下文是否處于中斷中,因?yàn)榛コ怄i的操作是不允許在中斷中進(jìn)行的。
判斷互斥鎖控制塊中的mutex->pend_nesting
成員變量是否為0
,為0表示互斥鎖處于開(kāi)鎖狀態(tài),調(diào)用mutex_fresh_owner_mark()
函數(shù)將獲取互斥鎖任務(wù)的相關(guān)信息保存到互斥鎖控制塊中,如mutex->pend_nesting
成員變量的值變?yōu)?code>1表示互斥鎖處于閉鎖狀態(tài),其他任務(wù)無(wú)法獲取,mutex->owner
成員變量指向當(dāng)前獲取互斥鎖的任務(wù)控制塊,mutex->owner_orig_prio
成員變量則是記錄當(dāng)前任務(wù)的優(yōu)先級(jí),最終使用tos_list_add()
函數(shù)將互斥鎖控制塊的mutex->owner_list
節(jié)點(diǎn)掛載到任務(wù)控制塊的task->mutex_own_list
列表中,任務(wù)獲取成功后返回K_ERR_NONE
。
如果互斥鎖控制塊中的mutex->pend_nesting
成員變量不為0
,則表示互斥鎖處于閉鎖狀態(tài),那么由于互斥鎖具有遞歸訪問(wèn)特性,那么會(huì)判斷一下是不是已經(jīng)持有互斥鎖的任務(wù)再次獲取互斥鎖(knl_is_self()
),因?yàn)檫@也是允許的,判斷一下mutex->pend_nesting
成員變量的值是否為(k_nesting_t)-1
,如果是則表示遞歸訪問(wèn)次數(shù)達(dá)到最大值,互斥鎖已經(jīng)溢出了,返回錯(cuò)誤代碼K_ERR_MUTEX_NESTING_OVERFLOW
。否則就將mutex->pend_nesting
成員變量的值加1,返回K_ERR_MUTEX_NESTING
表示遞歸獲取成功。
如果互斥鎖處于閉鎖狀態(tài),且當(dāng)前任務(wù)并未持有互斥鎖,看一下用戶指定的阻塞時(shí)間timeout
是否為不阻塞TOS_TIME_NOWAIT
,如果不阻塞則直接返回K_ERR_PEND_NOWAIT
錯(cuò)誤代碼。
如果調(diào)度器被鎖了knl_is_sched_locked()
,則無(wú)法進(jìn)行等待操作,返回錯(cuò)誤代碼K_ERR_PEND_SCHED_LOCKED
,畢竟需要切換任務(wù),調(diào)度器被鎖則無(wú)法切換任務(wù)。
最最最最重要的特性來(lái)了,在阻塞當(dāng)前任務(wù)之前,需要判斷一下當(dāng)前任務(wù)與持有互斥鎖的任務(wù)優(yōu)先級(jí)大小情況,如果當(dāng)前任務(wù)優(yōu)先級(jí)比持有互斥鎖任務(wù)優(yōu)先級(jí)大,則需要進(jìn)行優(yōu)先級(jí)繼承,臨時(shí)將持有互斥鎖任務(wù)的優(yōu)先級(jí)提升到當(dāng)前優(yōu)先級(jí),通過(guò)tos_task_prio_change()
函數(shù)進(jìn)行改變優(yōu)先級(jí)。
調(diào)用pend_task_block()
函數(shù)將任務(wù)阻塞,該函數(shù)實(shí)際上就是將任務(wù)從就緒列表中移除k_rdyq.task_list_head[task_prio]
,并且插入到等待列表中object->list
,如果等待的時(shí)間不是永久等待TOS_TIME_FOREVER
,還會(huì)將任務(wù)插入時(shí)間列表中k_tick_list
,阻塞時(shí)間為timeout
,然后進(jìn)行一次任務(wù)調(diào)度knl_sched()
。
當(dāng)程序能行到pend_state2errno()
時(shí),則表示任務(wù)等獲取到互斥鎖
,又或者等待發(fā)生了超時(shí)
,那么就調(diào)用pend_state2errno()
函數(shù)獲取一下任務(wù)的等待狀態(tài),看一下是哪種情況導(dǎo)致任務(wù)恢復(fù)運(yùn)行,如果任務(wù)已經(jīng)獲取到互斥鎖,那么需要調(diào)用mutex_new_owner_mark()
函數(shù)標(biāo)記一下獲取任務(wù)的信息,將獲取互斥鎖任務(wù)的相關(guān)信息保存到互斥鎖控制塊中。
注意:當(dāng)獲取互斥鎖的任務(wù)能從阻塞中恢復(fù)運(yùn)行,也不一定是獲取到互斥鎖,也可能是發(fā)生了超時(shí),因此在寫(xiě)程序的時(shí)候必須要判斷一下獲取的互斥鎖狀態(tài),如果返回值是K_ERR_NONE
與K_ERR_MUTEX_NESTING
則表示獲取成功!
__API__ k_err_t tos_mutex_pend_timed(k_mutex_t *mutex, k_tick_t timeout) { TOS_CPU_CPSR_ALLOC(); k_err_t err; TOS_PTR_SANITY_CHECK(mutex); TOS_IN_IRQ_CHECK(); #if TOS_CFG_OBJECT_VERIFY_EN > 0u if (!pend_object_verify(&mutex->pend_obj, PEND_TYPE_MUTEX)) { return K_ERR_OBJ_INVALID; } #endif TOS_CPU_INT_DISABLE(); if (mutex->pend_nesting == (k_nesting_t)0u) { // first come mutex_fresh_owner_mark(mutex, k_curr_task); TOS_CPU_INT_ENABLE(); return K_ERR_NONE; } if (knl_is_self(mutex->owner)) { // come again if (mutex->pend_nesting == (k_nesting_t)-1) { TOS_CPU_INT_ENABLE(); return K_ERR_MUTEX_NESTING_OVERFLOW; } ++mutex->pend_nesting; TOS_CPU_INT_ENABLE(); return K_ERR_MUTEX_NESTING; } if (timeout == TOS_TIME_NOWAIT) { // no wait, return immediately TOS_CPU_INT_ENABLE(); return K_ERR_PEND_NOWAIT; } if (knl_is_sched_locked()) { TOS_CPU_INT_ENABLE(); return K_ERR_PEND_SCHED_LOCKED; } if (mutex->owner->prio > k_curr_task->prio) { // PRIORITY INVERSION: // we are declaring a mutex, which's owner has a lower(numerically bigger) priority. // make owner the same priority with us. tos_task_prio_change(mutex->owner, k_curr_task->prio); } pend_task_block(k_curr_task, &mutex->pend_obj, timeout); TOS_CPU_INT_ENABLE(); knl_sched(); err = pend_state2errno(k_curr_task->pend_state); if (err == K_ERR_NONE) { // good, we are the owner now. TOS_CPU_INT_DISABLE(); mutex_new_owner_mark(mutex, k_curr_task); TOS_CPU_INT_ENABLE(); } return err; } __API__ k_err_t tos_mutex_pend(k_mutex_t *mutex) { TOS_PTR_SANITY_CHECK(mutex); return tos_mutex_pend_timed(mutex, TOS_TIME_FOREVER); }
mutex_fresh_owner_mark
與mutex_new_owner_mark()
函數(shù)的實(shí)現(xiàn):
__STATIC_INLINE__ void mutex_fresh_owner_mark(k_mutex_t *mutex, k_task_t *task) { mutex->pend_nesting = (k_nesting_t)1u; mutex->owner = task; mutex->owner_orig_prio = task->prio; tos_list_add(&mutex->owner_list, &task->mutex_own_list); } __STATIC_INLINE__ void mutex_new_owner_mark(k_mutex_t *mutex, k_task_t *task) { k_prio_t highest_pending_prio; mutex_fresh_owner_mark(mutex, task); // we own the mutex now, make sure our priority is higher than any one in the pend list. highest_pending_prio = pend_highest_prio_get(&mutex->pend_obj); if (task->prio > highest_pending_prio) { tos_task_prio_change(task, highest_pending_prio); } }
互斥鎖的釋放是不允許在中斷中釋放的,主要的原因是因?yàn)橹袛嘀袥](méi)有上下文的概念,所以中斷上下文不能持有,也不能釋放互斥鎖;互斥鎖有所屬
關(guān)系,只有持有互斥鎖的任務(wù)才能將互斥鎖釋放,而持有者是任務(wù)。
任務(wù)想要訪問(wèn)某個(gè)資源的時(shí)候,需要先獲取互斥鎖,然后進(jìn)行資源訪問(wèn),在任務(wù)使用完該資源的時(shí)候,必須要及時(shí)
釋放互斥鎖,這樣別的任務(wù)才能訪問(wèn)臨界資源。任務(wù)可以調(diào)用tos_mutex_post()
函數(shù)進(jìn)行釋放互斥鎖,當(dāng)互斥鎖處于開(kāi)鎖狀態(tài)時(shí)就表示我已經(jīng)用完了,別人可以獲取互斥鎖以訪問(wèn)臨界資源。
使用tos_mutex_post()
函數(shù)接口時(shí),只有已持有互斥鎖所有權(quán)的任務(wù)才能釋放它,當(dāng)任務(wù)調(diào)用tos_mutex_post()
函數(shù)時(shí)會(huì)將互斥鎖釋放一次,當(dāng)互斥鎖完全釋放完畢(mutex->pend_nesting
成員變量的值為0
)就變?yōu)殚_(kāi)鎖狀態(tài),等待獲取該互斥鎖的任務(wù)將被喚醒。如果任務(wù)的優(yōu)先級(jí)被互斥鎖的優(yōu)先級(jí)翻轉(zhuǎn)機(jī)制臨時(shí)提升,那么當(dāng)互斥鎖被釋放后,任務(wù)的優(yōu)先級(jí)將恢復(fù)為原本設(shè)定的優(yōu)先級(jí)。
TencentOS tiny
中可以只讓等待中的一個(gè)任務(wù)獲取到互斥鎖(在等待的任務(wù)中具有最高優(yōu)先級(jí))。
在tos_mutex_post()
函數(shù)中的處理也是非常簡(jiǎn)單明了的,其執(zhí)行思路如下:
首先進(jìn)行傳入的互斥鎖控制塊相關(guān)的參數(shù)檢測(cè),然后判斷一下是否是持有互斥鎖的任務(wù)來(lái)釋放互斥鎖,如果是則進(jìn)行釋放操作,如果不是則返回錯(cuò)誤代碼K_ERR_MUTEX_NOT_OWNER
。
將mutex->pend_nesting
成員變量的值減1,然后判斷它的值是否為0,如果不為0則表示當(dāng)前任務(wù)還是持有互斥鎖的,也無(wú)需進(jìn)行后續(xù)的操作,直接返回K_ERR_MUTEX_NESTING
。
如果mutex->pend_nesting
成員變量的值為0,則表示互斥鎖處于開(kāi)鎖狀態(tài),則需要調(diào)用mutex_old_owner_release()
函數(shù)完全釋放掉互斥鎖,在這個(gè)函數(shù)中會(huì)將互斥鎖控制塊的成員變量(如owner_list、owner、owner_orig_prio
等都設(shè)置為初始值),此外還有最重要的就是判斷一下任務(wù)是否發(fā)生過(guò)優(yōu)先級(jí)繼承,如果是則需要將任務(wù)恢復(fù)為原本的優(yōu)先級(jí),否則就無(wú)效理會(huì)。
調(diào)用pend_is_nopending()
函數(shù)判斷一下是否有任務(wù)在等待互斥鎖,如果沒(méi)有則返回K_ERR_NONE
表示釋放互斥鎖成功,因?yàn)榇藭r(shí)沒(méi)有喚醒任務(wù)也就無(wú)需任務(wù)調(diào)度,直接返回即可。
如果有任務(wù)在等待互斥鎖,則直接調(diào)用pend_wakeup_one()
函數(shù)喚醒一個(gè)等待任務(wù),這個(gè)任務(wù)在等待任務(wù)中是處于最高優(yōu)先級(jí)的,因?yàn)?code>TencentOS tiny 的等待任務(wù)是按照優(yōu)先級(jí)進(jìn)行排序。
進(jìn)行一次任務(wù)調(diào)度knl_sched()
。
__API__ k_err_t tos_mutex_post(k_mutex_t *mutex) { TOS_CPU_CPSR_ALLOC(); TOS_PTR_SANITY_CHECK(mutex); #if TOS_CFG_OBJECT_VERIFY_EN > 0u if (!pend_object_verify(&mutex->pend_obj, PEND_TYPE_MUTEX)) { return K_ERR_OBJ_INVALID; } #endif TOS_CPU_INT_DISABLE(); if (!knl_is_self(mutex->owner)) { TOS_CPU_INT_ENABLE(); return K_ERR_MUTEX_NOT_OWNER; } if (mutex->pend_nesting == (k_nesting_t)0u) { TOS_CPU_INT_ENABLE(); return K_ERR_MUTEX_NESTING_OVERFLOW; } --mutex->pend_nesting; if (mutex->pend_nesting > (k_nesting_t)0u) { TOS_CPU_INT_ENABLE(); return K_ERR_MUTEX_NESTING; } mutex_old_owner_release(mutex); if (pend_is_nopending(&mutex->pend_obj)) { TOS_CPU_INT_ENABLE(); return K_ERR_NONE; } pend_wakeup_one(&mutex->pend_obj, PEND_STATE_POST); TOS_CPU_INT_ENABLE(); knl_sched(); return K_ERR_NONE; }
持有互斥鎖的任務(wù)釋放互斥鎖mutex_old_owner_release()
。
__STATIC_INLINE__ void mutex_old_owner_release(k_mutex_t *mutex) { k_task_t *owner; owner = mutex->owner; tos_list_del(&mutex->owner_list); mutex->owner = K_NULL; // the right time comes! let's do it! if (owner->prio_pending != K_TASK_PRIO_INVALID) { tos_task_prio_change(owner, owner->prio_pending); owner->prio_pending = K_TASK_PRIO_INVALID; } else if (owner->prio != mutex->owner_orig_prio) { tos_task_prio_change(owner, mutex->owner_orig_prio); mutex->owner_orig_prio = K_TASK_PRIO_INVALID; } }
到此,關(guān)于“TencentOS tiny互斥鎖的原理以及實(shí)例用法”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!