溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

TencentOS tiny互斥鎖的原理以及實(shí)例用法

發(fā)布時(shí)間:2021-09-04 09:32:52 來(lái)源:億速云 閱讀:111 作者:chen 欄目:互聯(lián)網(wǎng)科技

這篇文章主要介紹“TencentOS tiny互斥鎖的原理以及實(shí)例用法”,在日常操作中,相信很多人在TencentOS tiny互斥鎖的原理以及實(shí)例用法問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”TencentOS tiny互斥鎖的原理以及實(shí)例用法”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(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),且危害是不可控的)。

優(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ì)的原則。

優(yōu)先級(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ù)搶占?;コ怄i的優(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接口。

互斥鎖的數(shù)據(jù)結(jié)構(gòu)

互斥鎖控制塊

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;

與互斥鎖相關(guān)的宏定義

tos_config.h中,使能互斥鎖的宏定義是TOS_CFG_MUTEX_EN

#define TOS_CFG_MUTEX_EN            1u

創(chuàng)建互斥鎖

系統(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ì)互斥鎖銷毀的處理流程如下:

  1. 調(diào)用pend_is_nopending()函數(shù)判斷一下是否有任務(wù)在等待互斥鎖

  2. 如果有則調(diào)用pend_wakeup_all()函數(shù)將這些任務(wù)喚醒,并且告知等待任務(wù)互斥鎖已經(jīng)被銷毀了(即設(shè)置任務(wù)控制塊中的等待狀態(tài)成員變量pend_statePEND_STATE_DESTROY)。

  3. 調(diào)用pend_object_deinit()函數(shù)將互斥鎖控制塊中的內(nèi)容清除,最主要的是將控制塊中的資源類型設(shè)置為PEND_TYPE_NONE,這樣子就無(wú)法使用這個(gè)互斥鎖了。

  4. mutex->pend_nesting成員變量恢復(fù)為默認(rèn)值0。

  5. 如果刪除的時(shí)候有任務(wù)持有互斥鎖,那么調(diào)用mutex_old_owner_release()函數(shù)將互斥鎖釋放。

  6. 進(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ò)程如下:

  1. 首先檢測(cè)傳入的參數(shù)是否正確,此處不僅會(huì)檢查互斥鎖控制塊的信息,還會(huì)調(diào)用TOS_IN_IRQ_CHECK()檢查上下文是否處于中斷中,因?yàn)榛コ怄i的操作是不允許在中斷中進(jìn)行的。

  2. 判斷互斥鎖控制塊中的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。

  3. 如果互斥鎖控制塊中的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表示遞歸獲取成功。

  4. 如果互斥鎖處于閉鎖狀態(tài),且當(dāng)前任務(wù)并未持有互斥鎖,看一下用戶指定的阻塞時(shí)間timeout是否為不阻塞TOS_TIME_NOWAIT,如果不阻塞則直接返回K_ERR_PEND_NOWAIT錯(cuò)誤代碼。

  5. 如果調(diào)度器被鎖了knl_is_sched_locked(),則無(wú)法進(jìn)行等待操作,返回錯(cuò)誤代碼K_ERR_PEND_SCHED_LOCKED,畢竟需要切換任務(wù),調(diào)度器被鎖則無(wú)法切換任務(wù)。

  6. 最最最最重要的特性來(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í)。

  7. 調(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()

  8. 當(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_NONEK_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_markmutex_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í)行思路如下:

  1. 首先進(jìn)行傳入的互斥鎖控制塊相關(guān)的參數(shù)檢測(cè),然后判斷一下是否是持有互斥鎖的任務(wù)來(lái)釋放互斥鎖,如果是則進(jìn)行釋放操作,如果不是則返回錯(cuò)誤代碼K_ERR_MUTEX_NOT_OWNER

  2. mutex->pend_nesting成員變量的值減1,然后判斷它的值是否為0,如果不為0則表示當(dāng)前任務(wù)還是持有互斥鎖的,也無(wú)需進(jìn)行后續(xù)的操作,直接返回K_ERR_MUTEX_NESTING。

  3. 如果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ì)。

  4. 調(diào)用pend_is_nopending()函數(shù)判斷一下是否有任務(wù)在等待互斥鎖,如果沒(méi)有則返回K_ERR_NONE表示釋放互斥鎖成功,因?yàn)榇藭r(shí)沒(méi)有喚醒任務(wù)也就無(wú)需任務(wù)調(diào)度,直接返回即可。

  5. 如果有任務(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)行排序。

  6. 進(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)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI