17 - FreeRTOS信號量API

  • FreeRTOS的信號量包括二進制信號量、計數信號量、互斥信號量(以後簡稱互斥量)和遞歸互斥信號量(以後簡稱遞歸互斥量)。我們可以把互斥量和遞歸互斥量看成特殊的信號量。
  • 信號量API函數實際上都是宏,它使用現有的隊列機制。這些宏定義在semphr.h文件中。如果使用信號量或者互斥量,需要包含semphr.h頭文件。
  • 二進制信號量、計數信號量和互斥量信號量的創建API函數是獨立的,但是獲取和釋放API函數都是相同的;遞歸互斥信號量的創建、獲取和釋放API函數都是獨立的。

創建二進制信號量

函數描述

SemaphoreHandle_t xSemaphoreCreateBinary( void );

  • 這個函數用於創建一個二進制信號量。二進制信號量要麼有效要麼無效,這也是爲什麼叫做二進制的原因。
  • 新創建的信號量處於無效狀態,這意味着使用API函數xSemaphoreTake()獲取信號之前,需要先給出信號。
  • 二進制信號量和互斥量非常相似,但也有細微的區別:互斥量具有優先級繼承機制,二進制信號量沒有這個機制。這使得二進制信號量更適合用於同步(任務之間或者任務和中斷之間),互斥量更適合互鎖。
  • 一旦獲得二進制信號量後不需要恢復,一個任務或中斷不斷的產生信號,而另一個任務不斷的取走這個信號,通過這樣的方式來實現同步。
  • 低優先級任務擁有互斥量的時候,如果另一個高優先級任務也企圖獲取這個信號量,則低優先級任務的優先級會被臨時提高,提高到和高優先級任務相同的優先級。這意味着互斥量必須要釋放,否則高優先級任務將不能獲取這個互斥量,並且那個擁有互斥量的低優先級任務也永遠不會被剝奪,這就是操作系統中的優先級翻轉。
  • 互斥量和二進制信號量都是SemaphoreHandle_t類型,並且可以用於任何具有這類參數的API函數中。

返回值

  • NULL:創建信號量失敗,因爲FreeRTOS堆棧不足。
  • 其它值:信號量創建成功。這個返回值存儲着信號量句柄。

用法舉例

SemaphoreHandle_t xSemaphore;

void vATask( void * pvParameters )
{
   /* 創建信號量 */
   xSemaphore = xSemaphoreCreateBinary();
   
   if( xSemaphore == NULL )
   {
   	/* 因堆棧不足,信號量創建失敗,這裏進行失敗處理*/
   }
   else
   {
   	/* 信號量可以使用。信號量句柄存儲在變量xSemahore中。
   	如果在這裏調用API函數xSemahoreTake()來獲取信號量,
   	則必然是失敗的,因爲創建的信號量初始是無效(空)的。*/
   }
}

創建計數信號量

函數描述

SemaphoreHandle_t xSemaphoreCreateCounting ( UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount )

  • 創建計數信號量,計數信號量通常用於以下兩種情況:
  1. 事件計數:在這種應用場合,每當事件發生,事件處理程序會“產生”一個信號量(信號量計數值會遞增),每當處理任務處理事件,會取走一個信號量(信號量計數值會遞減)。因此,事件發生或者事件被處理後,計數值是會變化的。
  2. 資源管理:在這種應用場合下,計數值表示有效資源的數目。爲了獲得資源,任務首先要獲得一個信號量—遞減信號量計數值。當計數值爲0時,表示沒有可用的資源。當佔有資源的任務完成,它會釋放這個資源,相應的信號量計數值會增一。計數值達到初始值(最大值)表示所有資源都可用。

參數描述

  • uxMaxCount:最大計數值,當信號到達這個值後,就不再增長了。
  • uxInitialCount:創建信號量時的初始值。

返回值

NULL表示信號量創建失敗,否則返回信號量句柄。

用法舉例

void vATask( void * pvParameters )
 {
     xSemaphoreHandle xSemaphore;
 
     // 必須先創建信號量,才能使用它
     // 信號量可以計數的最大值爲10,計數初始值爲0.
     xSemaphore = xSemaphoreCreateCounting( 10, 0 );
 
     if( xSemaphore != NULL )
     {
         // 信號量創建成功
         // 現在可以使用信號量了。
     }
 }

創建互斥量

函數描述

SemaphoreHandle_t xSemaphoreCreateMutex( void )

  • 創建互斥量。可以使用API函數xSemaphoreTake()和xSemaphoreGive()訪問互斥量,但是絕不可以用xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()訪問。
  • 二進制信號量和互斥量非常相似,但也有細微的區別:互斥量具有優先級繼承機制,二進制信號量沒有這個機制。這使得二進制信號量更適合用於同步(任務之間或者任務和中斷之間),互斥量更適合互鎖。
  • 一旦獲得二進制信號量後不需要恢復,一個任務或中斷不斷的產生信號,而另一個任務不斷的取走這個信號,通過這樣的方式來實現同步。
  • 低優先級任務擁有互斥量的時候,如果另一個高優先級任務也企圖獲取這個信號量,則低優先級任務的優先級會被臨時提高,提高到和高優先級任務相同的優先級。這意味着互斥量必須要釋放,否則高優先級任務將不能獲取這個互斥量,並且那個擁有互斥量的低優先級任務也永遠不會被剝奪,這就是操作系統中的優先級翻轉。
  • 互斥量和二進制信號量都是SemaphoreHandle_t類型,並且可以用於任何具有這類參數的API函數中。

返回值

NULL表示信號量創建失敗,否則返回信號量句柄。

用法舉例

xSemaphoreHandle xSemaphore;

voidvATask( void * pvParameters )
{
    // 互斥量在未創建之前是不可用的
    xSemaphore = xSemaphoreCreateMutex();
    if( xSemaphore != NULL )
    {
        // 創建成功
        // 在這裏可以使用這個互斥量了 
    }
}

創建遞歸互斥量

函數描述

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )

  • 用於創建遞歸互斥量。被創建的互斥量可以被API函數xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()使用,但不可以被API函數xSemaphoreTake()和xSemaphoreGive()使用。
  • 遞歸類型的互斥量可以被擁有者重複獲取。擁有互斥量的任務必須調用API函數xSemaphoreGiveRecursive()將擁有的遞歸互斥量全部釋放後,該信號量才真正被釋放。比如,一個任務成功獲取同一個互斥量5次,那麼這個任務要將這個互斥量釋放5次之後,其它任務才能獲取到它。
  • 遞歸互斥量具有優先級繼承機制,因此任務獲得一次信號後必須在使用完後做一個釋放操作。
  • 互斥量類型信號不可以用在中斷服務例程中。

返回值

NULL表示互斥量創建失敗,否則返回互斥量句柄。

用法舉例

xSemaphoreHandle xMutex;
 
void vATask( void * pvParameters )
{
    // 互斥量未創建前是不能被使用的
    xMutex = xSemaphoreCreateRecursiveMutex();
 
    if( xMutex != NULL )
    {
        // 創建成功
        // 在這裏創建互斥量
    }
}

刪除信號量

函數描述

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

  • 刪除信號量。如果有任務阻塞在這個信號量上,則這個信號量不要刪除。

參數描述

xSemaphore:信號量句柄

獲取信號量

函數描述

xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait)

  • 獲取信號量。信號量必須是通過API函數xSemaphoreCreateBinary()、xSemaphoreCreateCounting()和xSemaphoreCreateMutex()預先創建過的。注意,遞歸互斥量類型信號量不能使用該函數、不用在中斷服務程序中使用該函數。

參數描述

  • xSemaphore:信號量句柄
  • xTickToWait:信號量無效時,任務最多等待的時間,單位是系統節拍週期個數。使用宏portTICK_PERIOD_MS可以輔助將系統節拍個數轉化爲實際時間(以毫秒爲單位)。如果設置爲0,表示不是設置等待時間。如果INCLUDE_vTaskSuspend設置爲1,並且參數xTickToWait爲portMAX_DELAY則可以無限等待。

返回值

成功獲取到信號量返回pdTRUE,否則返回pdFALSE。

用法舉例

SemaphoreHandle_t xSemaphore = NULL;
 
/*這個任務創建信號量 */
void vATask( void * pvParameters )
{
    /*創建互斥型信號量,用於保護共享資源。*/
    xSemaphore = xSemaphoreCreateMutex();
}
 
/* 這個任務使用信號量 */
void vAnotherTask( void * pvParameters )
{
    /* ... 做其它事情. */
 
    if( xSemaphore != NULL )
    {
        /*如果信號量無效,則最多等待10個系統節拍週期。*/
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
        {
            /*到這裏我們獲取到信號量,現在可以訪問共享資源了*/
 
            /* ... */
 
            /* 完成訪問共享資源後,必須釋放信號量*/
            xSemaphoreGive( xSemaphore );
        }
        else
        {
            /* 沒有獲取到信號量,這裏處理異常情況。*/
        }
    }
}

獲取信號量(帶中斷保護)

函數描述

xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, signedBaseType_t *pxHigherPriorityTaskWoken)

  • API函數xSemaphoreTake()的另一版本,用於中斷服務程序。

參數描述

  • xSemaphore:信號量句柄
  • pxHigherPriorityTaskWoken:如果*pxHigherPriorityTaskWoken爲pdTRUE,則需要在中斷退出前手動進行一次上下文切換。從FreeRTOS V7.3.0開始,該參數爲可選參數,並可以設置爲NULL。

返回值

信號量成功獲取返回pdTRUE,否則返回pdFALSE。

獲取遞歸互斥量

函數描述

xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex, TickType_t xTicksToWait );

  • 獲取遞歸互斥信號量。互斥量必須是通過API函數xSemaphoreCreateRecursiveMutex()創建的類型。
  • 文件FreeRTOSConfig.h中的宏configUSE_RECURSIVE_MUTEXES必須設置成1,此函數纔有效。
  • 已經獲取遞歸互斥量的任務可以重複獲取該遞歸互斥量。使用xSemaphoreTakeRecursive() 函數成功獲取幾次遞歸互斥量,就要使用xSemaphoreGiveRecursive()函數返還幾次,在此之前遞歸互斥量都處於無效狀態。比如,某個任務成功獲取5次遞歸互斥量,那麼在它沒有返還5次該遞歸互斥量之前,這個互斥量對別的任務無效。

參數描述

  • xMutex:互斥量句柄,必須是使用API函數xSemaphoreCreateRecursiveMutex()返回的。
  • xTickToWait:互斥量無效時,任務最多等待的時間,單位是系統節拍週期個數。使用宏portTICK_PERIOD_MS可以輔助將系統節拍個數轉化爲實際時間(以毫秒爲單位)。如果設置爲0,表示不是設置等待時間。如果任務已經擁有信號量則xSemaphoreTakeRecursive()立即返回,不管xTickToWait是什麼值。

返回值

成功獲取遞歸互斥量返回pdTURE,否則返回pdFALSE。

用法舉例

SemaphoreHandle_t xMutex = NULL;
 
// 這個任務創建互斥量
void vATask( void *pvParameters )
{
    // 這個互斥量用於保護共享資源
    xMutex =xSemaphoreCreateRecursiveMutex();
}
 
//這個任務使用互斥量
void vAnotherTask( void *pvParameters )
{
    // ... 做其它事情.
 
    if( xMutex != NULL )
    {
        // 如果互斥量無效,則最多等待10系統時鐘節拍週期.   
        if(xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
        {
            // 到這裏我們成功獲取互斥量並可以訪問共享資源了
 
            // ...
            // 由於某種原因,某些代碼需要在一個任務中多次調用API函數
            // xSemaphoreTakeRecursive()。當然不會像本例中這樣連續式
            //調用,實際代碼會有更加複雜的結構
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
 
            // 我們獲取一個互斥量三次,所以我們要將這個互斥量釋放三次
            //它纔會變得有效。再一次說明,實際代碼可能會更加複雜。
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );
 
            // 到這裏,這個共享資源可以被其它任務使用了.
        }
        else
        {
            // 處理異常情況
        }
    }
}

釋放信號量

函數描述

xSemaphoreGive(SemaphoreHandle_t xSemaphore )

  • 用於釋放一個信號量。信號量必須是API函數xSemaphoreCreateBinary()、xSemaphoreCreateCounting()或xSemaphoreCreateMutex() 創建的。必須使用API函數xSemaphoreTake()獲取這個信號量。
  • 這個函數絕不可以在中斷服務例程中使用,可以使用帶中斷保護版本的API函數xSemaphoreGiveFromISR()來實現相同功能。
  • 這個函數不能用於使用API函數xSemaphoreCreateRecursiveMutex()所創建的遞歸互斥量。

參數描述

xSemaphore:信號量句柄。

返回值

信號量釋放成功返回pdTRUE,否則返回pdFALSE。

用法舉例

SemaphoreHandle_t xSemaphore = NULL;
 
voidvATask( void * pvParameters )
{
   // 創建一個互斥量,用來保護共享資源
   xSemaphore = xSemaphoreCreateMutex();
 
    if( xSemaphore != NULL )
    {
         if( xSemaphoreGive( xSemaphore ) != pdTRUE )
         {
              //我們希望這個函數調用失敗,因爲首先要獲取互斥量
         }
         // 獲取信號量,不等待
         if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
         {
              // 現在我們擁有互斥量,可以安全的訪問共享資源
              if( xSemaphoreGive( xSemaphore ) != pdTRUE )
              {
                   //我們不希望這個函數調用失敗,因爲我們必須
                   //要釋放已獲取的互斥量
              }
         }
     }
}

釋放信號量(帶中斷保護)

函數描述

xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
signed BaseType_t *pxHigherPriorityTaskWoken )

  • 釋放信號量。是API函數xSemaphoreGive()的另個版本,用於中斷服務程序。信號量必須是通過API函數xSemaphoreCreateBinary()或xSemaphoreCreateCounting()創建的。這裏沒有互斥量,是因爲互斥量不可以用在中斷服務程序中。

參數描述

  • xSemaphore:信號量句柄
  • pxHigherPriorityTaskWoken:如果*pxHigherPriorityTaskWoken爲pdTRUE,則需要在中斷退出前人爲的經行一次上下文切換。從FreeRTOS V7.3.0開始,該參數爲可選參數,並可以設置爲NULL。

返回值

成功釋放信號量返回pdTURE,否則返回errQUEUE_FULL。

用法舉例

#define LONG_TIME 0xffff
#define TICKS_TO_WAIT    10
 
SemaphoreHandle_t xSemaphore = NULL;
 
/* Repetitive task. */
void vATask( void * pvParameters )
{
    /* 我們使用信號量同步,所以先創建一個二進制信號量.必須確保
       在創建這個二進制信號量之前,中斷不會訪問它。*/
    xSemaphore = xSemaphoreCreateBinary();
 
    for( ;; )
    {
        /* 我們希望每產生10次定時器中斷,任務運行一次。*/
        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
        {
            /* 到這裏成功獲取到信號量*/
 
            ...
 
           /* 我們成功執行完一次,由於這是個死循環,所以任務仍會
               阻塞在等待信號量上。信號量由ISR釋放。*/
        }
    }
}
 
/* 定時器 ISR */
void vTimerISR( void * pvParameters )
{
    static unsigned char ucLocalTickCount = 0;
    static signed BaseType_txHigherPriorityTaskWoken;
 
    /*定時器中斷髮生 */
 
      ...執行其它代碼
 
    /*需要vATask() 運行嗎? */
    xHigherPriorityTaskWoken = pdFALSE;
    ucLocalTickCount++;
    if( ucLocalTickCount >= TICKS_TO_WAIT )
    {
        /* 釋放信號量,解除vATask任務阻塞狀態 */
        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
 
        /* 復位計數器 */
        ucLocalTickCount = 0;
    }
 
    /* 如果 xHigherPriorityTaskWoken 表達式爲真,需要執行一次上下文切換*/
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

釋放遞歸互斥量

函數描述

xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex )

  • 釋放一個遞歸互斥量。互斥量必須是使用 API函數xSemaphoreCreateRecursiveMutex()創建的。文件FreeRTOSConfig.h中宏configUSE_RECURSIVE_MUTEXES必須設置成1本函數纔有效。

參數描述

xMutex:互斥量句柄。必須是函數xSemaphoreCreateRecursiveMutex()返回的值。

返回值

如果遞歸互斥量釋放成功,返回pdTRUE。

用法舉例

見“8 獲取遞歸互斥量”。

獲取互斥量持有任務的句柄

函數描述

TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );

  • 返回互斥量持有任務的句柄(如果有的話),互斥量由參數xMutex指定。
  • 如果調用此函數的任務持有互斥量,那麼可以可靠的返回任務句柄,但是如果是別的任務持有互斥量,則不總可靠。
  • 文件FreeRTOSConfig.h中宏configUSE_MUTEXES必須設置成1本函數纔有效。

參數描述

xMutex:互斥量句柄

返回值

返回互斥量持有任務的句柄。如果參數xMutex不是互斥類型信號量或者雖然互斥量有效但這個互斥量不被任何任務持有則返回NULL。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章