精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

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

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

3天內不再提示

RT-Thread記錄(六、IPC機制之信號量互斥量事件集)

矜辰所致 ? 來源:矜辰所致 ? 作者:矜辰所致 ? 2022-06-21 10:40 ? 次閱讀
上文說到 RT-Thread 對臨界區的處理方式有多種,其中已經分析了關閉調度器和屏蔽中斷的方式,
本文就來學學另外的線程同步方式。

目錄

前言
一、IPC機制
二、信號
2.1 信號量控制塊
2.2 信號量操作
2.2.1 創建和刪除
2.2.2 初始化和脫離
2.2.3 獲取信號量
2.2.4 釋放信號量
2.2.5 信號量控制
2.3 示例(典型停車場模型)
三、互斥量
3.1 優先級翻轉
3.2 優先級繼承
3.3 互斥量控制塊
3.4 互斥量操作
3.2.1 創建和刪除
3.2.2 初始化和脫離
3.2.3 獲取互斥量
3.2.4 釋放互斥量
3.5 示例(優先級繼承)
四、事件集
4.1 事件集控制塊
4.2 事件集操作
4.2.1 創建和刪除
4.2.2 初始化和脫離
4.2.3 發送事件
4.2.4 接收事件
4.3 示例(邏輯與和邏輯或)
結語

前言

在我們專欄前面的文章中,已經學習過 RT-Thread 線程操作函數、軟件定時器、臨界區的保護,我們都進行了一些底層的分析,能讓我們更加理解 RT-Thread 的內核,但是也不要忽略了上層的函數使用 要理解 RT-Thread 面向對象的思想,對所有的這些線程啊,定時器,包括要介紹的信號量,郵箱這些,都是以 對象 來操作,直白的說來就是 對于所有這些對象,都是以結構體的形式來表示,然后通過對這個對象結構體的操作來進行的。


本文所要介紹的內容屬于 IPC機制,這些內容相對來說比較簡單,我們重點在于學會如何使用以及了解他們的使用場合。

本 RT-Thread 專欄記錄的開發環境:
RT-Thread記錄(一、版本開發環境及配合CubeMX) + http://www.nxhydt.com/d/1850333.html
RT-Thread記錄(二、RT-Thread內核啟動流程)+ http://www.nxhydt.com/d/1850347.html
RT-Thread 內核篇系列博文鏈接:
RT-Thread記錄(三、RT-Thread線程操作函數)+ http://www.nxhydt.com/d/1850351.html
RT-Thread記錄(四、RTT時鐘節拍和軟件定時器)+ http://www.nxhydt.com/d/1850554.html
RT-Thread記錄(五、RT-Thread 臨界區保護) + http://www.nxhydt.com/d/1850712.html


一、IPC機制

嵌入式操作系統中,運行代碼主要包括線程 和 ISR,在他們的運行過程中,因為應用或者多線程模型帶來的需求,有時候需要同步,有時候需要互斥,有時候也需要彼此交換數據。操作系統必須提供相應的機制來完成這些功能,這些機制統稱為 線程間通信(IPC機制)。

本文所要介紹的就是關于線程同步的信號量、互斥量、事件 也屬于 IPC機制。

RT-Thread 中的 IPC機制包括信號量、互斥量、事件、郵箱、消息隊列。對于學習 RT-Thread ,這些IPC機制我們必須要學會靈活的使用。

為什么要說一下這個IPC機制?

我們前面說到過,RT-Thread 面向對象的思想,所有的這些 IPC 機制都被當成一個對象,都有一個結構體控制塊,我們用信號量結構體來看一看:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

Kernel object有哪些,我們可以從基礎內核對象結構體定義下面的代碼找到:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_10,color_FFFFFF,t_70,g_se,x_16

本節說明了 RT-Thread 的 IPC 機制,同時通過 信號量的結構體控制塊再一次的認識了 RT-Thread 面向對象的設計思想。

在我的 FreeRTOS 專欄中,對于FreeRTOS 的信號量,互斥量,事件集做過說明和測試。在這個部分,實際上 RT-Thread 與 FreeRTOS 是類似的,都是一樣的思想。所以如果屬熟悉FreeRTOS的話,這部分是簡單的,我們要做的就是記錄一下 對象的控制塊,和操作函數,加以簡單的示例測試。

二、信號量

信號量官方的說明是:信號量是一種輕型的用于解決線程間同步問題的內核對象,線程可以獲取或釋放它,從而達到同步或互斥的目的。

信號量非常靈活,可以使用的場合也很多:

  • 比如 一個典型的應用場合就是停車位模型,總共有多少個車位,就是多少個信號量,入口進入一輛車信號量-1,出口離開一輛車信號量+1。
  • 比如 兩個線程之間的同步,信號量的值初始化成 0,而嘗試獲得該信號量的線程,一定需要等待另一個釋放信號量的線程先執行完。

在 FreeRTOS 中存在二值信號量,但是 RT-Thread 中已經沒有了,官方有說明:

在這里插入圖片描述

信號量記住一句話基本就可以,釋放一次信號量就+1,獲取一次就-1,如果信號量數據為0,那么嘗試獲取的線程就會掛機,直到有線程釋放信號量使得信號量大于0。

2.1 信號量控制塊

老規矩用源碼,解釋看注釋(使用起來也方便復制 ~ ~!):

#ifdef RT_USING_SEMAPHORE
/**
 * Semaphore structure
 * value 信號量的值,直接表明目前信號量的數量
 */
struct rt_semaphore
{
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */

    rt_uint16_t          value;                         /**< value of semaphore. */
    rt_uint16_t          reserved;                      /**< reserved field */
};
/*
rt_sem_t 是指向 semaphore 結構體的指針類型
*/
typedef struct rt_semaphore *rt_sem_t;
#endif

2.2 信號量操作

2.2.1 創建和刪除

同以前的線程那些一樣,動態的方式,先定義一個信號量結構體的指針變量,接收創建好的句柄。

創建信號量:

/*
參數的含義:
1、name 	信號量名稱
2、value 	信號量初始值
3、flag 	信號量標志,它可以取如下數值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回值:
信號量創建成功,返回信號量的控制塊指針
信號量創建失敗,返回RT_BULL 
*/
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)

對于最后的參數 flag,決定了當信號量不可用時(就是當信號量為0的時候),多個線程等待的排隊方式。只有RT_IPC_FLAG_FIFO (先進先出)或 RT_IPC_FLAG_PRIO(優先級等待)兩種 flag。

關于用哪一個,要看具體的情況,官方有特意說明:

poYBAGKxL4KAYSAVAABtsu0TwBo813.png

刪除信號量:

/*
參數:
sem 	rt_sem_create() 創建的信號量對象,信號量句柄
返回值:
RT_EOK 	刪除成功
*/
rt_err_t rt_sem_delete(rt_sem_t sem)

2.2.2 初始化和脫離

靜態的方式,先定義一個信號量結構體,然后對他進行初始化。

初始化信號量:

/**
參數的含義:
1、sem 		信號量對象的句柄,就是開始定義的信號量結構體變量
2、name 	信號量名稱
3、value 	信號量初始值
4、flag 	信號量標志,它可以取如下數值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回值:
RT_EOK 	初始化成功
 */
rt_err_t rt_sem_init(rt_sem_t    sem,
                     const char *name,
                     rt_uint32_t value,
                     rt_uint8_t  flag)

脫離信號量:

/*
參數:
sem 	信號量對象的句柄
返回值:
RT_EOK 	脫離成功
*/
rt_err_t rt_sem_detach(rt_sem_t sem);

2.2.3 獲取信號量

當信號量值大于零時,線程將獲得信號量,并且相應的信號量值會減 1。

/**
參數:
1、sem 		信號量對象的句柄
2、time 	指定的等待時間,單位是操作系統時鐘節拍(OS Tick)
返回值:
RT_EOK 			成功獲得信號量
-RT_ETIMEOUT 	超時依然未獲得信號量
-RT_ERROR 		其他錯誤
 */
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)

注意!要等待的時間是系統時鐘節拍(OS Tick)。

無等待獲取信號量

//就是上面獲取的等待時間為0的方式
rt_err_t rt_sem_trytake(rt_sem_t sem)
{
    return rt_sem_take(sem, 0);
}

當線程申請的信號量資源實例為0時,直接返回 - RT_ETIMEOUT。

2.2.4 釋放信號量

釋放信號量可以使得該信號量+1,如果有線程在等待這個信號量,可以喚醒這個線程。

/**
參數:
sem 	信號量對象的句柄
返回值:
RT_EOK 	成功釋放信號量
 */
rt_err_t rt_sem_release(rt_sem_t sem)

2.2.5 信號量控制

信號量控制函數,用來重置信號量,使得信號量恢復為設定的值:


/**
 * This function can get or set some extra attributions of a semaphore object.
參數:
sem 	信號量對象的句柄
cmd    信號量控制命令 ,支持命令:RT_IPC_CMD_RESET 
arg    暫時不知道
返回值:
RT_EOK 	成功釋放信號量

 */
rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg)
{
    rt_ubase_t level;

    /* parameter check */
    RT_ASSERT(sem != RT_NULL);
    RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);

    if (cmd == RT_IPC_CMD_RESET)
    {
        rt_ubase_t value;

        /* get value */
        value = (rt_ubase_t)arg;
        /* disable interrupt */
        level = rt_hw_interrupt_disable();

        /* resume all waiting thread */
        rt_ipc_list_resume_all(&sem->parent.suspend_thread);

        /* set new value */
        sem->value = (rt_uint16_t)value;

        /* enable interrupt */
        rt_hw_interrupt_enable(level);

        rt_schedule();

        return RT_EOK;
    }

    return -RT_ERROR;
}

使用示例:

rt_err_t result;
rt_uint32_t value;

value = 10; /* 重置的值,即重置為10 */
result = rt_sem_control(sem, RT_IPC_CMD_RESET, (void*)value)

/* 重置為0 */
rt_sem_control(sem, RT_IPC_CMD_RESET, RT_NULL)

對sem重置后,會先把sem上掛起的所有任務進行喚醒(任務的error是-RT_ERROR),然后把sem的值會重新初始化成設定的值。

在官方論壇有如下說明:
在rt_sem_release后使用rt_sem_control的目的是因為在某些應用中必須rt_sem_take和rt_sem_release依次出現,而不允許rt_sem_release被連續多次調用,一旦出現這種情況會被認為是出現了異常,通過調用rt_sem_control接口來重新初始化 sem_ack恢復異常。

2.3 示例(典型停車場模型)

前面說到過,信號量非常靈活,可以使用的場合也很多,官方也有很多例子,我們這里做個典型的示例
— 停車場模型(前面用截圖做解釋,后面會附帶源碼)。

示例中,我們使用兩個不同的按鍵來模擬車輛的進出,但是考慮到我們還沒有學設備和驅動,沒有添加按鍵驅動,所以我們用古老的方式來實現按鍵操作:

按鍵key3,代表車輛離開:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

按鍵key2,代表車輛進入:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

信號量的創建,初始10個車位:

pYYBAGKxL4OAAKKMAAAexk-g3H8704.png

當然不要忘了,車輛進入和車輛離開(兩個按鍵)是需要兩個線程的。

我們來看看測試效果,說明如圖:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_8,color_FFFFFF,t_70,g_se,x_16

注意上圖測試最后的細節,雖然 one car get out! 但是打印出來的停車位還是0,可以這么理解,key3_thread_entry線程釋放了信號量以后還沒來得及打印,等待信號量的線程key2_thread_entry就獲取到了信號量。

具體的分析需要看rt_sem_release函數源碼,里面會判斷是否需要值+1,以及是否需要調度:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

附上上面測試代碼:

/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-02-16     RT-Thread    first version
 */

#include 
#include "main.h"
#include "usart.h"
#include "gpio.h"

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include 

static struct rt_thread led1_thread;    //led1線程
static char led1_thread_stack[256];

static rt_thread_t led2_thread = RT_NULL; //led2線程

static rt_thread_t key2_thread = RT_NULL; //

static rt_thread_t key3_thread = RT_NULL; //

rt_sem_t mysem;


static void led1_thread_entry(void *par){
    while(1){
        LED1_ON;
        rt_thread_mdelay(1000);
        LED1_OFF;
        rt_thread_mdelay(1000);
    }
}

static void led2_thread_entry(void *par){
    while(1){
        LED2_ON;
        rt_thread_mdelay(500);
        LED2_OFF;
        rt_thread_mdelay(500);
    }
}

static void key2_thread_entry(void *par){
    static rt_err_t result;
    while(1){
        if(key2_read == 0){
            rt_thread_mdelay(10); //去抖動
            if(key2_read == 0){
                result = rt_sem_take(mysem, 1000);
                if (result != RT_EOK)
                {
                    rt_kprintf("the is no parking spaces now...\r\n");
                }
                else
                {
                    rt_kprintf("one car get in!,we have %d parking spaces now...\r\n",mysem->value);
                }
                while(key2_read == 0){rt_thread_mdelay(10);}
            }
        }
        rt_thread_mdelay(1);
    }
}

static void key3_thread_entry(void *par){
    while(1){
        if(key3_read == 0){
            rt_thread_mdelay(10); //去抖動
            if(key3_read == 0){
                if(mysem->value < 10){
                    rt_sem_release(mysem);
                    rt_kprintf("one car get out!,we have %d parking spaces now...\r\n",mysem->value);
                }
                while(key3_read == 0){rt_thread_mdelay(10);} //去抖動
            }
        }
        rt_thread_mdelay(1);
    }
}
int main(void)
{
    MX_GPIO_Init();
    MX_USART1_UART_Init();


    rt_err_t rst2;
    rst2 = rt_thread_init(&led1_thread,
                        "led1_blink ",
                        led1_thread_entry,
                        RT_NULL,
                        &led1_thread_stack[0],
                        sizeof(led1_thread_stack),
                        RT_THREAD_PRIORITY_MAX -1,
                        50);

    if(rst2 == RT_EOK){
        rt_thread_startup(&led1_thread);
    }


    mysem = rt_sem_create("my_sem1", 10, RT_IPC_FLAG_FIFO);
    if(RT_NULL == mysem){
        LOG_E("create sem failed!...\n");
    }
    else LOG_D("we have 10 parking spaces now...\n");

    key2_thread = rt_thread_create("key2_control",
                                key2_thread_entry,
                                RT_NULL,
                                512,
                                RT_THREAD_PRIORITY_MAX -2,
                                50);

        /* 如果獲得線程控制塊,啟動這個線程 */
        if (key2_thread != RT_NULL)
            rt_thread_startup(key2_thread);

     key3_thread = rt_thread_create("key3_control",
                                key3_thread_entry,
                                RT_NULL,
                                512,
                                RT_THREAD_PRIORITY_MAX -2,
                                50);

        /* 如果獲得線程控制塊,啟動這個線程 */
        if (key3_thread != RT_NULL)
            rt_thread_startup(key3_thread);
    return RT_EOK;
}


void led2_Blink(){
    led2_thread = rt_thread_create("led2_blink",
                            led2_thread_entry,
                            RT_NULL,
                            256,
                            RT_THREAD_PRIORITY_MAX -1,
                            50);

    /* 如果獲得線程控制塊,啟動這個線程 */
    if (led2_thread != RT_NULL)
        rt_thread_startup(led2_thread);
}

MSH_CMD_EXPORT(led2_Blink, Led2 sample);

三、互斥量

互斥量是一種特殊的二值信號量。互斥量的狀態只有兩種,開鎖或閉鎖(兩種狀態值)。

互斥量支持遞歸,持有該互斥量的線程也能夠再次獲得這個鎖而不被掛起。自己能夠再次獲得互斥量。

互斥量可以解決優先級翻轉問題,它能夠實現優先級繼承。

互斥量互斥量不能在中斷服務例程中使用。

3.1 優先級翻轉

優先級翻轉,我以前寫過:

poYBAGKxL4SAUwRIAABs3UaqWig058.png

再用官方的圖加深理解:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

3.2 優先級繼承

優先級繼承,以前也寫過:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

再用官方的圖加深理解:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

需要切記的是互斥量不能在中斷服務例程中使用。

3.3 互斥量控制塊

#ifdef RT_USING_MUTEX
/**
 * Mutual exclusion (mutex) structure
 * parent 				繼承ipc類
 * value 				互斥量的值
 * original_priority 	持有線程的原始優先級
 * hold 				持有線程的持有次數,可以多次獲得
 * *owner				當前擁有互斥量的線程
 */
struct rt_mutex
{
    struct rt_ipc_object parent;              /**< inherit from ipc_object */
    rt_uint16_t          value;                /**< value of mutex */
    rt_uint8_t           original_priority;    /**< priority of last thread hold the mutex */
    rt_uint8_t           hold;                 /**< numbers of thread hold the mutex */

    struct rt_thread    *owner;               /**< current owner of mutex */
};
/* rt_mutext_t 為指向互斥量結構體的指針類型  */
typedef struct rt_mutex *rt_mutex_t;
#endif

3.4 互斥量操作

3.4.1 創建和刪除

先定義一個指向互斥量結構體的指針變量,接收創建好的句柄。

創建互斥量:

/**
參數的含義:
1、name 	互斥量名稱
2、flag 	該標志已經作廢,無論用戶選擇 RT_IPC_FLAG_PRIO 還是 RT_IPC_FLAG_FIFO,
			內核均按照 RT_IPC_FLAG_PRIO 處理
返回值:
互斥量創建成功,返回互斥量的控制塊指針
互斥量創建失敗,返回RT_BULL 
 */
rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)

刪除互斥量:

/**
參數:
mutex	互斥量對象的句柄
返回值:
RT_EOK 	刪除成
 */
rt_err_t rt_mutex_delete(rt_mutex_t mutex)

3.4.2 初始化和脫離

靜態的方式,先定義一個互斥量結構體,然后對他進行初始化。

初始化互斥量:

/**
參數的含義:
1、mutex 互斥量對象的句柄,指向互斥量對象的內存塊,開始定義的結構體
2、name 	互斥量名稱
3、flag 	該標志已經作廢,按照 RT_IPC_FLAG_PRIO (優先級)處理
返回值:
RT_EOK 	初始化成功
 */
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)

脫離互斥量:

/**
參數:
mutex	互斥量對象的句柄
返回值:
RT_EOK 	成功
 */
rt_err_t rt_mutex_detach(rt_mutex_t mutex)

3.4.3 獲取互斥量

一個時刻一個互斥量只能被一個線程持有。

如果互斥量沒有被其他線程控制,那么申請該互斥量的線程將成功獲得該互斥量。如果互斥量已經被當前線程線程控制,則該互斥量的持有計數加 1,當前線程也不會掛起等待。

/**
參數:
1、mutex	互斥量對象的句柄
2、time 	指定的等待時間,單位是操作系統時鐘節拍(OS Tick)
返回值:
RT_EOK 			成功獲得互斥量
-RT_ETIMEOUT 	超時依然未獲得互斥量
-RT_ERROR 		獲取失敗
 */
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)

3.4.4 釋放互斥量

在獲得互斥量后,應該盡可能的快釋放互斥量。

/**
參數:
mutex 	互斥量對象的句
返回值:
RT_EOK 	成功
 */
rt_err_t rt_mutex_release(rt_mutex_t mutex)

3.5 示例(優先級繼承)

互斥量做一個簡單的示例,但是即便簡單,也能體現出優先級繼承這個機制。

示例中,我們使用兩個按鍵,key2按鍵,按一次獲取互斥量,再按一次釋放互斥量,打印自己初始優先級,當前優先級,互斥量占有線程優先級這幾個量。key3按鍵,按一次,獲取互斥量,立馬就釋放,也打印幾個優先級。

互斥量的創建,和兩個線程的優先級:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_15,color_FFFFFF,t_70,g_se,x_16

key2操作:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

key3操作:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

測試結果說明圖:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

示例中為了更好的演示并沒有快進快出,實際使用還是需要快進快出,除非你自己就是有這種特出需求。

還有一個細節,就是 RT-Thread 中對象的 名字,只能顯示8個字符長度,長了會截斷,并不影響使用。

四、事件集

事件集這部分與 FreeRTOS 基本一樣。

事件集主要用于線程間的同步,它的特點是可以實現一對多,多對多的同步。即一個線程與多個事件的關系可設置為:其中任意一個事件喚醒線程,或幾個事件都到達后才喚醒線程進行后續的處理;同樣,事件也可以是多個線程同步多個事件。

RT-Thread 定義的事件集有以下特點:

  • 事件只與線程相關,事件間相互獨立:每個線程可擁有 32 個事件標志,采用一個 32 bit 無符號整型數進行記錄,每一個 bit 代表一個事件;
  • 事件僅用于同步,不提供數據傳輸功能;
  • 事件無排隊性,即多次向線程發送同一事件 (如果線程還未來得及讀走),其效果等同于只發送一次。

4.1 事件集控制塊

#ifdef RT_USING_EVENT
/**
 * flag defintions in event
 * 邏輯與
 * 邏輯或
 * 清除標志位
 */
#define RT_EVENT_FLAG_AND               0x01            /**< logic and */
#define RT_EVENT_FLAG_OR                0x02            /**< logic or */
#define RT_EVENT_FLAG_CLEAR             0x04            /**< clear flag */

/*
 * event structure
 * set:事件集合,每一 bit 表示 1 個事件,bit 位的值可以標記某事件是否發生
 */
struct rt_event
{
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */

    rt_uint32_t          set;                           /**< event set */
};
/* rt_event_t 是指向事件結構體的指針類型  */
typedef struct rt_event *rt_event_t;
#endif

4.2 事件集操作

4.2.1 創建和刪除

先定義一個指向事件集結構體的指針變量,接收創建好的句柄。

創建事件集:

/**
參數的含義:
1、name 	事件集的名稱
2、flag 	事件集的標志,它可以取如下數值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO理
返回值:
事件集創建成功,返回事件集的控制塊指針
事件集創建失敗,返回RT_BULL 
 */
rt_event_t rt_event_create(const char *name, rt_uint8_t flag)

flag 使用哪一個,解釋和信號量一樣,可參考信號量創建部分說明。

刪除事件集:

/**
參數:
event	事件集對象的句柄
返回值:
RT_EOK 	成功
 */
rt_err_t rt_event_delete(rt_event_t event)

4.2.2 初始化和脫離

靜態的方式,先定義一個事件集結構體,然后對他進行初始化。

初始化事件集:

/**
參數的含義:
1、event	事件集對象的句柄
2、name 	事件集的名稱
3、flag 	事件集的標志,它可以取如下數值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回值:
RT_EOK 	初始化成功
 */
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag)

脫離事件集:

/**
參數:
event	事件集對象的句柄
返回值:
RT_EOK 	成功
 */
rt_err_t rt_event_detach(rt_event_t event)

4.2.3 發送事件

發送事件函數可以發送事件集中的一個或多個事件。

/**
參數的含義:
1、event	事件集對象的句柄
2、set		發送的一個或多個事件的標志值
返回值:
RT_EOK 	成功
 */
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set)

4.2.4 接收事件

內核使用 32 位的無符號整數來標識事件集,它的每一位代表一個事件,因此一個事件集對象可同時等待接收 32 個事件,內核可以通過指定選擇參數 “邏輯與” 或“邏輯或”來選擇如何激活線程。

/**
參數的含義:
1、event		事件集對象的句柄
2、set			接收線程感的事件
3、option 		接收選項,可取的值為
#define RT_EVENT_FLAG_AND               0x01       邏輯與    
#define RT_EVENT_FLAG_OR                0x02       邏輯或    
#define RT_EVENT_FLAG_CLEAR             0x04     選擇清除重置事件標志位       
4、timeout		指定超時時間
5、recved		指向接收到的事件,如果不在意,可以使用 NULL
返回值:
RT_EOK 			成功
-RT_ETIMEOUT 	超時
-RT_ERROR 		錯誤
 */
rt_err_t rt_event_recv(rt_event_t   event,
                       rt_uint32_t  set,
                       rt_uint8_t   option,
                       rt_int32_t   timeout,
                       rt_uint32_t *recved)

4.3 示例(邏輯與和邏輯或)

事件集通過示例可以很好的理解怎么使用,我們示例中,用按鈕發送事件,其他線程接收事件,進行對應的處理。

按鍵操作:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16

線程邏輯或處理:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_19,color_FFFFFF,t_70,g_se,x_16

邏輯或測試結果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_16,color_FFFFFF,t_70,g_se,x_16

線程邏輯與處理:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_19,color_FFFFFF,t_70,g_se,x_16

邏輯與測試結果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_17,color_FFFFFF,t_70,g_se,x_16

結語

本文雖然只是介紹了信號量、互斥量和事件集這幾個比較簡單的線程同步操作,但是最終完成了后發現內容還是很多的。

洋洋灑灑這么多字,最終看下來自己還是挺滿意的,希望我把該表述的都表達清楚了,希望大家多多提意見,讓博主能給大家帶來更好的文章。

那么下一篇的 RT-Thread 記錄,就要來說說與線程通訊 有關的 郵箱、消息隊列和信號內容了。

謝謝!

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

    關注

    3

    文章

    345

    瀏覽量

    51828
  • RT-Thread
    +關注

    關注

    31

    文章

    1272

    瀏覽量

    39923
  • 信號量
    +關注

    關注

    0

    文章

    53

    瀏覽量

    8314
收藏 人收藏

    評論

    相關推薦

    RT-thread內核互斥

    便于恢復。hold: 表示此互斥鎖被同一線程成功take的次數,一般情況下一個線程只會take一次互斥鎖,但rt-thread也允許線程重復take同一線程,此時hold的值就用來做記錄
    發表于 03-06 17:23

    第15章 互斥信號量

    轉rtx操作系統 本章節開始講解RTX的另一個重要的資源共享機制---互斥信號量(Mutex,即Mutual Exclusion的縮寫)。注意,建議初學者學習完上個章節的信號量后再學習
    發表于 10-06 16:40

    RT-Thread信號量刪除后釋放信號量跟獲取信號量還是成功

    RT-Thread中創建了一個動態的信號量,運行10次這個線程后刪除這個動態信號量,但是問題是10次后他再次釋放信號量跟獲取信號量還是成功的
    發表于 01-15 05:04

    互斥源碼分析測試

    文章目錄互斥源碼分析測試參考資料:RTT官網文檔關鍵字:分析RT-Thread源碼、stm32、RTOS、互斥
    發表于 08-24 06:01

    淺析RT-Thread中事件的工作機制

    RT-Thread 中的事件,也就是其他 RTOS 中的事件標志組。事件也是線程(任務)間同步的一種機制。前面介紹的兩種線程間同步的方式(信號量
    發表于 04-11 15:31

    【原創精選】RT-Thread征文精選技術文章合集

    RT-Thread記錄(五、RT-Thread 臨界區保護)RT-Thread記錄
    發表于 07-26 14:56

    RT-Thread操作系統互斥的使用方法與場合介紹

    可以進入。互斥工作機制互斥信號量不同的是:擁有互斥
    發表于 08-03 11:26

    Rt-thread里面的mem.c函數保護lfree全局變量為什么用信號量

    Rt-thread 里面的mem.c函數保護lfree全局變量為什么用信號量而不是互斥信號量,用信號量保護全局變量不怕造成線程優先級翻轉嗎
    發表于 08-08 10:43

    RT-Thread互斥優先級問題求解

    RT Thread優先級問題,官網視頻,互斥一節,明明是線程2的優先級比線程1高,但線程1會優先運行,不知是有什么機理還是Bug?經反復測試發現,將線程優先級配置到接近線程優先級的最
    發表于 12-09 15:43

    信號量互斥在使用過程中會存在這樣的死鎖嗎?

    如果A線程已經獲取了信號量互斥,但此時B線程打斷了A線程,信號量互斥沒有釋放,并且在B線
    發表于 01-10 15:37

    Linux IPC System V 信號量

    semctl() //刪除信號量 ftok()//獲取key值, key值是System V IPC的標識符,成功返回key,失敗返回-1設errno//同
    發表于 04-02 14:46 ?319次閱讀

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

    1 、互 斥 信 號 1.1 互斥信號量的概念及其作用 互斥信號量的主要作用是對資源實現互斥
    的頭像 發表于 10-22 11:57 ?1.1w次閱讀
    詳解<b class='flag-5'>互斥</b><b class='flag-5'>信號量</b>的概念和運行

    RT-Thread隱藏的寶藏completion

    completion 直接翻譯過來是完成,所以我們可以稱 rt_completion 為 完成。在 RT-Thread 的文檔中心 中講線程間同步時,介紹了 信號量...
    發表于 01-25 18:54 ?0次下載
    <b class='flag-5'>RT-Thread</b>隱藏的寶藏<b class='flag-5'>之</b>completion

    Free RTOS的互斥信號量

    二進制信號量互斥非常相似,但確實有一些細微的區別。互斥體包含優先級繼承機制,而二進制信號量
    的頭像 發表于 02-10 15:36 ?1125次閱讀
    Free RTOS的<b class='flag-5'>互斥</b><b class='flag-5'>信號量</b>

    使用Linux信號量實現互斥點燈

    信號量常用于控制對共享資源的訪問,有計數型信號量和二值信號量之分。初始化時信號量值大于1的,就是計數型信號量,計數型
    的頭像 發表于 04-13 15:12 ?788次閱讀
    使用Linux<b class='flag-5'>信號量</b>實現<b class='flag-5'>互斥</b>點燈