一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

詳解互斥信號量的概念和運行

開源嵌入式 ? 來源:開源嵌入式 ? 作者:開源嵌入式 ? 2020-10-22 11:57 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1 、互 斥 信 號 量

1.1 互斥信號量的概念及其作用

互斥信號量的主要作用是對資源實現互斥訪問,使用二值信號量也可以實現互斥訪問的功能,不過互斥信號量與二值信號量有區(qū)別。下面我們先舉一個通過二值信號量實現資源獨享,即互斥訪問的例子,讓大家有一個形象的認識,進而引出要講解的互斥信號量。

運行條件:

讓兩個任務 Task1 和 Task2 都運行串口打印函數 printf,這里我們就通過二值信號量實現對函數printf 的互斥訪問。如果不對函數 printf 進行互斥訪問,串口打印容易出現亂碼。

用計數信號量實現二值信號量只需將計數信號量的初始值設置為 1 即可。

代碼實現:

創(chuàng)建二值信號量

static SemaphoreHandle_t xSemaphore = NULL; * 函 數 名: AppObjCreate * 功能說明: 創(chuàng)建任務通信機制 * 形 參: 無 * 返 回 值: 無 static void AppObjCreate (void) {/* 創(chuàng)建二值信號量,首次創(chuàng)建信號量計數值是 0 */xSemaphore = xSemaphoreCreateBinary();if(xSemaphore == NULL) {/* 沒有創(chuàng)建成功,用戶可以在這里加入創(chuàng)建失敗的處理機制 */}/* 先釋放一次,將初始值改為 1,利用二值信號量實現互斥功能 */xSemaphoreGive(xSemaphore); }

? 通過二值信號量實現對 printf 函數互斥訪問的兩個任務

static void vTaskLED(void *pvParameters) { TickType_t xLastWakeTime;const TickType_t xFrequency = 300;/* 獲取當前的系統(tǒng)時間 */xLastWakeTime = xTaskGetTickCount();while(1) {/* 通過二值信號量實現資源互斥訪問,永久等待直到資源可用 */xSemaphoreTake(xSemaphore, portMAX_DELAY); printf("任務 vTaskLED 在運行 "); bsp_LedToggle(1); bsp_LedToggle(4); xSemaphoreGive(xSemaphore);/* vTaskDelayUntil 是絕對延遲,vTaskDelay 是相對延遲。*/vTaskDelayUntil(&xLastWakeTime, xFrequency); } } * 函 數 名: vTaskMsgPro * 功能說明: 實現對串口的互斥訪問 * 形 參: pvParameters 是在創(chuàng)建該任務時傳遞的形參 * 返 回 值: 無 * 優(yōu) 先 級: 3 static void vTaskMsgPro(void *pvParameters) { TickType_t xLastWakeTime; const TickType_t xFrequency = 300; /* 獲取當前的系統(tǒng)時間 */xLastWakeTime = xTaskGetTickCount(); while(1) { /* 通過二值信號量實現資源互斥訪問,永久等待直到資源可用 */ xSemaphoreTake(xSemaphore, portMAX_DELAY); printf("任務 vTaskMsgPro 在運行 "); bsp_LedToggle(2); bsp_LedToggle(3); xSemaphoreGive(xSemaphore); /* vTaskDelayUntil 是絕對延遲,vTaskDelay 是相對延遲。*/ vTaskDelayUntil(&xLastWakeTime, xFrequency); } }

有了上面二值信號量的認識之后,互斥信號量與二值信號量又有什么區(qū)別呢?互斥信號量可以防止優(yōu)先級翻轉,而二值信號量不支持,下面我們就講解一下優(yōu)先級翻轉問題。

1.2 優(yōu)先級翻轉問題

下面我們通過如下的框圖來說明一下優(yōu)先級翻轉的問題,讓大家有一個形象的認識。


運行條件:

創(chuàng)建 3 個任務 Task1,Task2 和 Task3,優(yōu)先級分別為 3,2,1。也就是 Task1 的優(yōu)先級最高。
任務 Task1 和 Task3 互斥訪問串口打印 printf,采用二值信號實現互斥訪問。
起初 Task3 通過二值信號量正在調用 printf,被任務 Task1 搶占,開始執(zhí)行任務 Task1,也就是上圖的起始位置。

運行過程描述如下:
任務 Task1 運行的過程需要調用函數 printf,發(fā)現任務 Task3 正在調用,任務 Task1 會被掛起,等待 Task3 釋放函數 printf。
在調度器的作用下,任務 Task3 得到運行,Task3 運行的過程中,由于任務 Task2 就緒,搶占了 Task3的運行。優(yōu)先級翻轉問題就出在這里了,從任務執(zhí)行的現象上看,任務 Task1 需要等待 Task2 執(zhí)行完畢才有機會得到執(zhí)行,這個與搶占式調度正好反了,正常情況下應該是高優(yōu)先級任務搶占低優(yōu)先級任務的執(zhí)行,這里成了高優(yōu)先級任務 Task1 等待低優(yōu)先級任務 Task2 完成。所以這種情況被稱之為優(yōu)先級翻轉問題。
任務 Task2 執(zhí)行完畢后,任務 Task3 恢復執(zhí)行,Task3 釋放互斥資源后,任務 Task1 得到互斥資源,從而可以繼續(xù)執(zhí)行。

上面就是一個產生優(yōu)先級翻轉問題的現象。

1.3 FreeRTOS 互斥信號量的實現

FreeRTOS 互斥信號量是怎么實現的呢?其實相對于二值信號量,互斥信號量就是解決了一下優(yōu)先級翻轉的問題。下面我們通過如下的框圖來說明一下 FreeRTOS 互斥信號量的實現,讓大家有一個形象的認識。

運行條件:

創(chuàng)建 2 個任務 Task1 和 Task2,優(yōu)先級分別為 1 和 3,也就是任務 Task2 的優(yōu)先級最高。
任務 Task1 和 Task2 互斥訪問串口打印 printf。
使用 FreeRTOS 的互斥信號量實現串口打印 printf 的互斥訪問。

運行過程描述如下:

低優(yōu)先級任務 Task1 執(zhí)行過程中先獲得互斥資源 printf 的執(zhí)行。此時任務 Task2 搶占了任務 Task1的執(zhí)行,任務 Task1 被掛起。任務 Task2 得到執(zhí)行。
任務 Task2 執(zhí)行過程中也需要調用互斥資源,但是發(fā)現任務 Task1 正在訪問,此時任務 Task1 的優(yōu)先級會被提升到與 Task2 同一個優(yōu)先級,也就是優(yōu)先級 3,這個就是所謂的優(yōu)先級繼承(Priority inheritance),這樣就有效地防止了優(yōu)先級翻轉問題。任務 Task2 被掛起,任務 Task1 有新的優(yōu)先級繼續(xù)執(zhí)行。
任務 Task1 執(zhí)行完畢并釋放互斥資源后,優(yōu)先級恢復到原來的水平。由于互斥資源可以使用,任務Task2 獲得互斥資源后開始執(zhí)行。

上面就是一個簡單的 FreeRTOS 互斥信號量的實現過程。

1.4 FreeRTOS 中斷方式互斥信號量的實現

互斥信號量僅支持用在 FreeRTOS 的任務中,中斷函數中不可使用。

2 互 斥 信 號 量 API 函 數

使用如下 18 個函數可以實現 FreeRTOS 的信號量(含計數信號量,二值信號量和互斥信號):
? xSemaphoreCreateBinary()
? xSemaphoreCreateBinaryStatic()
? vSemaphoreCreateBinary()
? xSemaphoreCreateCounting()
? xSemaphoreCreateCountingStatic()
? xSemaphoreCreateMutex()
? xSemaphoreCreateMutexStatic()
? xSem'CreateRecursiveMutex()
? xSem'CreateRecursiveMutexStatic()
? vSemaphoreDelete()
? xSemaphoreGetMutexHolder()
? uxSemaphoreGetCount()
? xSemaphoreTake()
? xSemaphoreTakeFromISR()
? xSemaphoreTakeRecursive()
? xSemaphoreGive()
? xSemaphoreGiveRecursive()

? xSemaphoreGiveFromISR()

關于這 18 個函數的講解及其使用方法可以看 FreeRTOS 在線版手冊:

2.1 函數 xSemaphoreCreateMutex

函數原型:
SemaphoreHandle_t xSemaphoreCreateMutex( void )
函數描述:
函數 xSemaphoreCreateMutex 用于創(chuàng)建互斥信號量。
返回值,如果創(chuàng)建成功會返回互斥信號量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不足,無法為此互斥信號量提供所需的空間會返回 NULL。

使用這個函數要注意以下問題:
1. 此函數是基于函數 xQueueCreateMutex 實現的:
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
函數 xQueueCreateMutex 的實現是基于消息隊列函數 xQueueGenericCreate 實現的。
2. 使用此函數要在 FreeRTOSConfig.h 文件中使能宏定義:
#define configUSE_MUTEXES 1

使用舉例:

static SemaphoreHandle_t xMutex = NULL; * 函 數 名: AppObjCreate * 功能說明: 創(chuàng)建任務通信機制 * 形 參: 無 * 返 回 值: 無 static void AppObjCreate (void) {/* 創(chuàng)建互斥信號量 */xMutex = xSemaphoreCreateMutex(); if(xSemaphore == NULL) {/* 沒有創(chuàng)建成功,用戶可以在這里加入創(chuàng)建失敗的處理機制 */} }

2.2 函數 xSemaphoreGive

函數原型:
xSemaphoreGive( SemaphoreHandle_t xSemaphore ); /* 信號量句柄 */
函數描述:
函數 xSemaphoreGive 用于在任務代碼中釋放信號量。
第 1 個參數是信號量句柄。
返回值,如果信號量釋放成功返回 pdTRUE,否則返回 pdFALSE,因為信號量的實現是基于消息隊列,返回失敗的主要原因是消息隊列已經滿了。
使用這個函數要注意以下問題:
1. 此函數是基于消息隊列函數 xQueueGenericSend 實現的:
#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME,queueSEND_TO_BACK )
2. 此函數是用于任務代碼中調用的,故不可以在中斷服務程序中調用此函數,中斷服務程序中使用的是xSemaphoreGiveFromISR。

3. 使用此函數前,一定要保證用函數 xSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或者 xSemaphoreCreateCounting()創(chuàng)建了信號量。
4. 此函數不支持使用 xSemaphoreCreateRecursiveMutex()創(chuàng)建的信號量。

2.3 函數 xSemaphoreTake

函數原型:
xSemaphoreTake( SemaphoreHandle_t xSemaphore, /* 信號量句柄 */
TickType_t xTicksToWait ); /* 等待信號量可用的最大等待時間 */
函數描述:
函數 xSemaphoreTake 用于在任務代碼中獲取信號量。
第 1 個參數是信號量句柄。
第 2 個參數是沒有信號量可用時,等待信號量可用的最大等待時間,單位系統(tǒng)時鐘節(jié)拍。

返回值,如果創(chuàng)建成功會獲取信號量返回 pdTRUE,否則返回 pdFALSE。
使用這個函數要注意以下問題:
1. 此函數是用于任務代碼中調用的,故不可以在中斷服務程序中調用此函數,中斷服務程序使用的是xSemaphoreTakeFromISR。
2. 如果消息隊列為空且第 2 個參數為 0,那么此函數會立即返回。
3. 如果用戶將 FreeRTOSConfig.h 文件中的宏定義 INCLUDE_vTaskSuspend 配置為 1 且第 2 個參數配置為 portMAX_DELAY,那么此函數會永久等待直到信號量可用。

使用舉例:

static SemaphoreHandle_t xMutex = NULL; * 函 數 名: vTaskLED * 功能說明: 實現串口的互斥訪問,防止多個任務同時訪問造成串口打印亂碼 * 形 參: pvParameters 是在創(chuàng)建該任務時傳遞的形參 * 返 回 值: 無 * 優(yōu) 先 級: 2 static void vTaskLED(void *pvParameters) { TickType_t xLastWakeTime;const TickType_t xFrequency = 200;/* 獲取當前的系統(tǒng)時間 */xLastWakeTime = xTaskGetTickCount();while(1) {/* 互斥信號量,xSemaphoreTake 和 xSemaphoreGive 一定要成對的調用 */xSemaphoreTake(xMutex, portMAX_DELAY); printf("任務 vTaskLED 在運行 "); bsp_LedToggle(2); bsp_LedToggle(3); xSemaphoreGive(xMutex);/* vTaskDelayUntil 是絕對延遲,vTaskDelay 是相對延遲。*/vTaskDelayUntil(&xLastWakeTime, xFrequency); } }

互斥信號量,xSemaphoreTake 和 xSemaphoreGive 一定要成對的調用

經過測試,互斥信號量是可以被其他任務釋放的,但是我們最好不要這么做,因為官方推薦的就是在同一個任務中接收和釋放。如果在其他任務釋放,不僅僅會讓代碼整體邏輯變得復雜,還會給使用和維護這套API的人帶來困難。遵守規(guī)范,總是好的。

裸機編程的時候,我經常想一個問題,就是怎么做到當一個標志位觸發(fā)的時候,立即執(zhí)行某個操作,如同實現標志中斷一樣,在os編程之后,我們就可以讓一個優(yōu)先級最高任務一直等待某個信號量,如果獲得信號量,就執(zhí)行某個操作,實現類似標志位中斷的作用(當然,要想正真做到中斷效果,那就需要屏蔽所有可屏蔽中斷,而臨界區(qū)就可以做到)。

再說一下遞歸互斥信號量:遞歸互斥信號量,其實就是互斥信號量里面嵌套互斥信號量

使用舉例:

static void vTaskMsgPro(void *pvParameters) { TickType_t xLastWakeTime; const TickType_t xFrequency = 1500; /* 獲取當前的系統(tǒng)時間 */ xLastWakeTime = xTaskGetTickCount(); while(1) {/* 遞歸互斥信號量,其實就是互斥信號量里面嵌套互斥信號量 */ xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY); {//假如這里是被保護的資源,第1層被保護的資源,用戶可以在這里添加被保護資源 printf("任務vTaskMsgPro在運行,第1層被保護的資源,用戶可以在這里添加被保護資源 "); /* 第1層被保護的資源里面嵌套被保護的資源 */ xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY); { //假如這里是被保護的資源,第2層被保護的資源,用戶可以在這里添加被保護資源 printf("任務vTaskMsgPro在運行,第2層被保護的資源,用戶可以在這里添加被保護資源 "); /* 第2層被保護的資源里面嵌套被保護的資源 */ xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY); { printf("任務vTaskMsgPro在運行,第3層被保護的資源,用戶可以在這里添加被保護資源 "); bsp_LedToggle(1); bsp_LedToggle(4); } xSemaphoreGiveRecursive(xRecursiveMutex); } xSemaphoreGiveRecursive(xRecursiveMutex); } xSemaphoreGiveRecursive(xRecursiveMutex); /* vTaskDelayUntil是絕對延遲,vTaskDelay是相對延遲。*/ vTaskDelayUntil(&xLastWakeTime, xFrequency); } }

可以前面的那個官方文檔那樣用if判斷傳遞共享量:

也可以用我們遞歸互斥信號量里面的portMAX_DELAY指定永久等待,即xSemaphoreTakeRecursive返回的不是pdTRUE時,會一直等待信號量,直到有信號量來才執(zhí)行后面的語句。

責任編輯:PSY

原文標題:嵌入式系統(tǒng)FreeRTOS — 互斥信號量

文章出處:【微信公眾號:開源嵌入式】歡迎添加關注!文章轉載請注明出處。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯系本站處理。 舉報投訴
  • 嵌入式系統(tǒng)

    關注

    41

    文章

    3683

    瀏覽量

    131397
  • FreeRTOS
    +關注

    關注

    12

    文章

    493

    瀏覽量

    64356
  • 互斥信號量
    +關注

    關注

    0

    文章

    3

    瀏覽量

    2133
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    鴻蒙應用px,vp,fp概念詳解

    【HarmonyOS 5】鴻蒙應用px,vp,fp概念詳解 ##鴻蒙開發(fā)能力 ##HarmonyOS SDK應用服務##鴻蒙金融類應用 (金融理財# 一、前言 目前的鴻蒙開發(fā)者,大多數是從前端或者
    的頭像 發(fā)表于 07-07 11:48 ?175次閱讀
    鴻蒙應用px,vp,fp<b class='flag-5'>概念</b><b class='flag-5'>詳解</b>

    4G模擬對傳模塊,無線通訊,實時同步模擬信號

    。 一、基本概念 4G模擬對傳模塊,一種支持第四代移動通信網絡技術(4GLTE)的無線通信模塊,同時具備模擬信號的傳輸能力。通過將模擬
    的頭像 發(fā)表于 04-10 16:54 ?311次閱讀

    混合信號設計的概念、挑戰(zhàn)與發(fā)展趨勢

    本文介紹了集成電路設計領域中混合信號設計的概念、挑戰(zhàn)與發(fā)展趨勢。
    的頭像 發(fā)表于 04-01 10:30 ?608次閱讀

    嵌入式開發(fā)避坑指南|FreeRTOS的5個\"反直覺\"小技巧

    ;整個系統(tǒng)! 類型選擇: 場景 推薦類型 關鍵特性 資源計數(如內存池) 二值信號量 不可遞歸獲取 互斥訪問(如串口) 互斥鎖(Mutex) 支持優(yōu)先級繼承 事件通知(如按鍵) 計數信號量
    發(fā)表于 03-20 13:57

    FreeRTOS(V9.0)中創(chuàng)建信號量的函數都沒有被定義,因此用不了,怎么解決

    問題背景:我想要使用信號量,結果查找了整個工程都沒有創(chuàng)建信號量的函數。我還以為是我自己移植有問題,因此還特地下載了其他人移植好的工程進行編程。結果也沒有創(chuàng)建信號量的函數。不論是二值信號量
    發(fā)表于 03-13 09:30

    基于OpenHarmony標準系統(tǒng)的C++公共基礎類庫案例:Semaphore

    1、程序簡介該程序是基于OpenHarmony標準系統(tǒng)的C++公共基礎類庫的線程處理:Sempahore。本案例完成如下工作:(1)無名信號量使用方法定義1個無名信號量,1個供無名信號量管理
    的頭像 發(fā)表于 02-10 18:08 ?339次閱讀
    基于OpenHarmony標準系統(tǒng)的C++公共基礎類庫案例:Semaphore

    模擬信號的應用和優(yōu)缺點

    在現代工業(yè)自動化、環(huán)境監(jiān)測、科學研究等領域,模擬信號作為一種基本且重要的信號形式,扮演著不可或缺的角色。本文將對模擬信號進行深度解析,探
    的頭像 發(fā)表于 02-03 11:26 ?1113次閱讀

    DM368視頻前端信號采集詳解

    電子發(fā)燒友網站提供《DM368視頻前端信號采集詳解.pdf》資料免費下載
    發(fā)表于 09-29 10:58 ?0次下載
    DM368視頻前端<b class='flag-5'>信號</b>采集<b class='flag-5'>詳解</b>

    【開源鴻蒙】使用QEMU運行OpenHarmony輕系統(tǒng)

    本文將會介紹如何從源碼安裝QEMU 6.2.0,以及如何使用QEMU運行OpenHarmony輕系統(tǒng)。通過本文,你將會對QEMU和OpenHarmony輕系統(tǒng)又一個初步的認知,并對如何使用QEMU又一個初步的理解和體會。
    的頭像 發(fā)表于 09-14 08:51 ?1357次閱讀
    【開源鴻蒙】使用QEMU<b class='flag-5'>運行</b>OpenHarmony輕<b class='flag-5'>量</b>系統(tǒng)

    開關和模擬的基本概念、特點及應用

    開關和模擬是電氣工程和自動化控制領域中的兩個重要概念。開關通常指二進制信號,即只有兩種狀態(tài):開或關,高或低,1或0。模擬
    的頭像 發(fā)表于 08-30 11:10 ?2346次閱讀

    熱控信號和保護裝置能否正常運行

    熱控信號和保護裝置是確保電力系統(tǒng)安全、穩(wěn)定、經濟運行的重要手段。它們在電力系統(tǒng)中起著至關重要的作用,能夠及時檢測和處理各種異常情況,防止設備損壞和系統(tǒng)故障。 一、熱控信號和保護裝置的基本概念
    的頭像 發(fā)表于 08-27 11:12 ?729次閱讀

    基于OpenHarmony標準系統(tǒng)的C++公共基礎類庫案例:Semaphore

    ,運行如下命令: hb build -f 將鏡像燒錄到開發(fā)板中。 5、運行結果 5.1、無名信號量 運行結果如下: # utils_noname_semaphore thread_0
    發(fā)表于 08-14 16:38

    PLC對模擬信號的處理過程及方法 詳解

    模擬信號是自動化過程控制系統(tǒng)中最基本的過程信號(壓力、溫度、流量等)輸入形式。系統(tǒng)中的過程信號通過變送器,將這些檢測信號轉換為統(tǒng)一的電壓、
    的頭像 發(fā)表于 07-30 16:31 ?972次閱讀
    PLC對模擬<b class='flag-5'>量</b><b class='flag-5'>信號</b>的處理過程及方法 <b class='flag-5'>詳解</b>版

    傳感器的基本概念、工作原理及輸出類型

    傳感器是一種將物理或化學轉換為電信號的裝置,廣泛應用于工業(yè)、農業(yè)、醫(yī)療、環(huán)保等領域。傳感器的輸出可以是模擬或數字量,這取決于傳感器的類型和應用場景。 一、傳感器的基本
    的頭像 發(fā)表于 07-25 09:34 ?4597次閱讀

    模擬信號和開關信號的重要區(qū)別以及具體應用

    。 ? ? ??但是對于一些剛入門學習PLC和變頻器的師傅而言就不同了,對于開關信號和模擬信號非常容易弄混亂,甚至分不清楚哪些設備是開關
    的頭像 發(fā)表于 07-21 16:09 ?1147次閱讀
    模擬<b class='flag-5'>量</b><b class='flag-5'>信號</b>和開關<b class='flag-5'>量</b><b class='flag-5'>信號</b>的重要區(qū)別以及具體應用