天天看點

uCos中的信号量機制

文章目錄

    • 1. 背景
    • 2. 概述
      • 2.1. 主要機制及應用
      • 2.2. 同步或通信的基本方式
    • 3. 信号量
      • 3.1. 主要機制及應用
      • 3.2. 分類
      • 3.3. 互斥信号量
        • 3.3.1. 嵌套(遞歸)資源通路
        • 3.3.2. 删除安全
      • 3.4. 各種互斥機制的比較
      • 3.5. 二值信号量
      • 3.6. 計數信号量
    • 4. uC/OS-II中實作
      • 4.1. 建立信号量
      • 4.2. 擷取(申請)信号量
      • 4.3. 釋放信号量
      • 4.4. 删除信号量

1. 背景

前段時間老師上課講到了uC/OS中的信号量機制,是以我想要結合《μC/OS-III源碼分析筆記》和中國大學MOOC-電子科技大學《嵌入式系統及應用》PPT寫一篇筆記對這部分的内容進行總結。

2. 概述

2.1. 主要機制及應用

uCos中的信号量機制

Figure 1. 模型 \text{Figure 1. 模型} Figure 1. 模型

  • 在單處理器平台上,嵌入式作業系統核心提供的同步、互斥與通信機制

    主要包括:

    • 信号量(semaphore),用于互斥與同步。
    • 事件(組)(event group),用于同步。
    • 郵箱(mailbox)、消息隊列(message queue),用于消息通信。
    • 異步信号(asynchronous signal),用于同步。
    • 管道(pipe),提供非結構化資料交換(通信)和實作同步。
  • 單處理器或多處理器系統中,還有其它一些機制也可用于同步與通信:
    • 全局變量
    • 共享記憶體
    • Sockets
    • 遠端過程調用(Remote Procedure Call)

2.2. 同步或通信的基本方式

uCos中的信号量機制

Figure 2. 同步或通信的基本方式 \text{Figure 2. 同步或通信的基本方式} Figure 2. 同步或通信的基本方式

3. 信号量

3.1. 主要機制及應用

  • 在單處理器平台上,嵌入式作業系統核心提供的同步、互斥與通信機制 主要包括:
    • 信号量(semaphore),用于互斥與同步。
    • 事件(組)(event group),用于同步。
    • 郵箱(mailbox)、消息隊列(message queue),用于消息通信。
    • 異步信号(asynchronous signal),用于同步。
    • 管道(pipe),提供非結構化資料交換(通信)和實作同步。
  • 單處理器或多處理器系統中,還有其它一些機制也可用于同步與通信: 全局變量、共享記憶體、Sockets、遠端過程調用(Remote Procedure Call)。

3.2. 分類

信号量用于實作任務與任務之間、任務與中斷處理程式之間的同步與 互斥。信号量一般分為三種:

  • 互斥信号量:用于解決互斥問題,可能會引起優先級反轉問題。
  • 二值信号量:用于解決同步問題
  • 計數信号量:用于解決資源計數問題

3.3. 互斥信号量

  • 用互斥信号量保護的代碼區稱作

    臨界區

    ,臨界區代碼通常用 于對共享資源的通路。
  • 共享資源可能是一段存儲器空間、一個資料結構或I/O裝置,也 可能是被兩個或多個并發任務共享的任何内容。
  • 使用

    互斥信号量可以實作對共享資源的串行通路

    ,保證隻有成功 地擷取互斥信号量的任務才能夠釋放它。
  • 互斥信号量基本特點:互斥信号量的值被初始化成1,最多隻有 一個任務可以進入

    臨界區

uCos中的信号量機制

Figure 3. 互斥信号量狀态圖 \text{Figure 3. 互斥信号量狀态圖} Figure 3. 互斥信号量狀态圖

  • 互斥信号量是一種特殊的二值信号量,一般它

    支援所有權、遞歸訪 問、任務删除安全

    和一些避免

    優先級反轉、饑餓、死鎖等

    互斥所固 有問題的協定。
  • 所有權:當一個任務通過擷取互斥信号量而将其鎖定時,得到該互 斥信号量的所有權。相反,當一個任務釋放信号量時,失去對其的 所有權。

3.3.1. 嵌套(遞歸)資源通路

  • 嵌套(遞歸)資源通路:如果Task1調用RoutineA,而RoutineA 又調用RoutineB,并且三者通路相同的共享資源,就發生了遞歸共 享資源的通路同步問題。
  • 一個遞歸的互斥信号量允許嵌套鎖定互斥信号量,而不引起死鎖。
  • 每個擷取信号量的調用必須與釋放信号量的調用相比對。
  • 用于

    同步的信号量不支援嵌套通路

    ,否則任務會被永久阻塞。

3.3.2. 删除安全

  • 删除安全:在一個受信号量保護的臨界區,經常需要保護在臨界區執行的任務不會被意外地删除。
  • 删除一個在臨界區執行的任務可能引起意想不到的後果,造成信号量不可用或資源破壞。
  • 解決方法:提供

    任務保護

    解除任務保護

    原語對。

3.4. 各種互斥機制的比較

uCos中的信号量機制

Figure 4. 各種互斥機制的比較 \text{Figure 4. 各種互斥機制的比較} Figure 4. 各種互斥機制的比較

3.5. 二值信号量

二值信号量主要用于任務與任務之間、任務與中斷服務程式之間的 同步。二值信号量的初始值為0,表示同步事件尚未産生。

uCos中的信号量機制

Figure 5. 二值信号量狀态圖 \text{Figure 5. 二值信号量狀态圖} Figure 5. 二值信号量狀态圖

用二值信号量實作任務間雙向同步時,申請者和釋放者不是同一個任務,與互斥信号量不同。互斥信号量必須誰申請,誰釋放。

uCos中的信号量機制

Figure 6. 用二值信号量實作任務間雙向同步 \text{Figure 6. 用二值信号量實作任務間雙向同步} Figure 6. 用二值信号量實作任務間雙向同步

3.6. 計數信号量

  • 計數信号量用于控制系統中共享資源的多個執行個體的使用,允許多個 任務同時通路同一種資源的多個執行個體。
  • 計數信号量被初始化為一個非負整數n,即該種共享資源的數目。
uCos中的信号量機制

Figure 7. 計數信号量狀态圖 \text{Figure 7. 計數信号量狀态圖} Figure 7. 計數信号量狀态圖

uCos中的信号量機制

Figure 8. 有界緩沖問題1 \text{Figure 8. 有界緩沖問題1} Figure 8. 有界緩沖問題1

uCos中的信号量機制

Figure 9. 有界緩沖問題 \text{Figure 9. 有界緩沖問題} Figure 9. 有界緩沖問題

4. uC/OS-II中實作

4.1. 建立信号量

OS_EVENT* OSSemCreate(INT16U cnt)
{
    OS_EVENT* pevent;
    pevent = OSEventFreeList; //從空閑事件控制塊鍊中取得一個ECB
    if (OSEventFreeList != (OS_EVENT*)0) {
        OSEventFreeList = (OS_EVENT*) { OSEventFreeList->OSEventPtr; }
        if (pevent != (OS_EVENT*)0) { //初始化ECB的各個域
            pevent->OSEventType = OS_EVENT_TYPE_SEM; //事件類型為信号量
            //信号量的初始計數值
            pevent->OSEventCnt = cnt;
            pevent->OSEventPtr = (void*)0;
            OS_EventWaitListInit(pevent); //初始化等待任務清單
        }
    }
    return (pevent); //調用者需檢查傳回值,如果為NULL則表示建立失敗
}
           

說明:這是針對計數信号量和二值信号量的實作。 μC/OS-II中互斥 信号量的實作參見源碼os_mutex.c檔案。

uCos中的信号量機制

Figure 10. 基本流程 \text{Figure 10. 基本流程} Figure 10. 基本流程

4.2. 擷取(申請)信号量

void OSSemPend(OS_EVENT* pevent, INT16U timeout, INT8U* err)
{
    //信号量值大于0,成功獲得信号量并傳回
    if (pevent->OSEventCnt > 0) {
        pevent->OSEventCnt--;
        *err = OS_NO_ERR;
        return;
    }
    //設定任務狀态為等待信号量
    OSTCBCur->OSTCBStat |= OS_STAT_SEM;
    //設定等待時限
    OSTCBCur->OSTCBDly = timeout;
    //将任務放置到信号量的等待清單中
    OS_EventTaskWait(pevent);
    //核心實施任務排程,系統切換到另一就緒任務執行
    OS_Sched();
    //判斷任務恢複執行的原因,如果等待時限逾時但仍然未獲得信号量,則傳回逾時資訊
    if (OSTCBCur->OSTCBStat & OS_STAT_SEM) {
        OSEventTO(pevent);
        *err = OS_TIMEOUT;
        return;
    }
    OSTCBCur->OSTCBEventPtr = (OS_EVENT*)0;
    *err = OS_NO_ERR; //任務由于獲得信号量而恢複執行,本調用成功傳回
}
           

說明:這裡同樣是針對計數信号量和二值信号量的實作。

OSSemAccept

也可以實作擷取(申請)信号量的功能。

INT16U OSSemAccept(OS_EVENT* pevent)
{
    INT16U cnt;
    cnt = pevent->OSEventCnt;
    if (cnt > 0) {
        pevent->OSEventCnt--;
        }
    return (cnt);
}
           
  • 功能:無等待的擷取或申請一個信号量。
  • 注意:即使不能成功獲得信号量(傳回值為0),調用者也不 會被阻塞。此函數可以在中斷處理程式中使用。

4.3. 釋放信号量

uCos中的信号量機制

Figure 12. 釋放信号量 \text{Figure 12. 釋放信号量} Figure 12. 釋放信号量

INT8U OSSemPost(OS_EVENT* pevent)
{

    if (pevent->OSEventGrp != 0x00) { //如果有任務在等待該信号量
        OS_EventTaskRdy(pevent, (void*)0, OS_STAT_SEM); //使等待清單中優先級最高的任務就緒
        OS_Sched(); //核心實施任務排程
        return (OS_NO_ERR); //成功傳回
    }
    if (pevent->OSEventCnt < 65535) { //如果沒有任務等待該信号量,并且信号量的值未溢出
        pevent->OSEventCnt++; //信号量的值加1
        return (OS_NO_ERR); //成功傳回
    }
    return (OS_SEM_OVF); //信号量溢出
}
           

4.4. 删除信号量

uCos中的信号量機制

Figure 13. 删除信号量 \text{Figure 13. 删除信号量} Figure 13. 删除信号量

OS_EVENT* OSSemDel(OS_EVENT* pevent, INT8U opt, INT8U* err)
{
    BOOLEAN tasks_waiting;
    if(pevent->OSEventGrp!=0x00{//根據是否有任務在等待信号量設定等待标志
            tasks_waiting = TRUE;
    }else{
            tasks_waiting = FALSE;
    }
    switch(opt){
        case OS_DEL_NO_PEND: //如果有任務等待信号量則不删除信号量
            if(task_waiting==FALSE{ //沒有任務等待,釋放ECB回空閑鍊
                pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
                pevent->OSEventPtr = OSEventFreeList;
                OSEventFreeList = pevent; //調整空閑ECB鍊頭指針
                *err = OS_NO_ERR;
                return ((OS_EVENT)0);
            }else{
                *err = OS_ERR_TASK_WAITING; //有任務等待,删除信号量失敗
                return (pevent);
            }
        case OS_DEL_ALWAYS: //無論有無任務等待都删除信号量
                //将等待清單中的每個任務都設定成就緒
                while (pevent->OSEventGrp != 0x00) {
                OS_EventTaskRdy(pevent, (void*)0, OS_STAT_SEM);
                }
                pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
                pevent->OSEventFreeList;
                OSEventFreeList = pevent; //釋放該信号量的ECB回空閑控制塊鍊
                if (tasks_waiting == TRUE) {
                OS_Sched();
                } //如果之前有任務等待信号量,核心實施任務排程
                *err = OS_NO_ERR;
                return ((OS_EVENT*)0);
        default:
                *err = OS_ERR_INVALID_OPT;
                return (pevent);
        }
}



           

聯系郵箱:[email protected]

CSDN:https://me.csdn.net/qq_41729780

知乎:https://zhuanlan.zhihu.com/c_1225417532351741952

公衆号:複雜網絡與機器學習

歡迎關注/轉載,有問題歡迎通過郵箱交流。

uCos中的信号量機制

繼續閱讀