当前位置: 首页 > news >正文

FreeRTOS与信号量(四)

FreeRTOS与硬件定时器中断(二)-CSDN博客

上面这篇文章中我已经介绍过了二值信号量的使用方法。

在后续我会专门出一期介绍信号量与互斥锁、与事件标志组等之间的关系,以及三者之间在现实场景开发中是如何选择并且协调使用的文章。

文章(二)已经介绍过二值信号量的使用方法,这篇文章我只简单补充一下计数信号量和互斥信号量的内容,以及介绍二值信号量与互斥信号量的关系

一、信号量介绍

在 RTOS(实时操作系统)中,信号量(Semaphore) 是一种用于协调多任务协作的机制,本质上是一个 “计数器”+“等待队列” 的组合,核心作用是解决任务同步资源共享问题。

1. 信号量核心作用是解决任务同步,那么,什么是任务同步

“任务同步” 指的是:通过信号量协调两个任务(或事件)的执行时序,让一个任务的动作(如定时器触发的事件)成为另一个任务执行特定操作的 “开关”,确保两者在时间上 “步调一致”

简单说,就是 “你不触发,我就不执行;你一触发,我就立即响应”—— 这就是 “同步” 的核心;而 “触发” 则是指通过信号量的 “释放” 动作,启动后续任务的操作。

在文章(二)中使用的二值信号量例程就是例子,就是解决了任务的同步触发,实现了在不影响任务调度运行的条件下,使用硬件定时器中断。

2. 信号量的三种类型

二值信号量:状态只有 0 或 1,像 “开关”,用于任务间同步(比如 A 做完通知 B 开始)。

计数信号量:计数器范围 0 到 N,像 “资源计数器”,控制最多 N 个任务同时使用有限资源(如 5 个缓冲区)。

互斥信号量:特殊二值信号量,带优先级继承机制,专门保护独占资源(如传感器),避免多个任务同时访问,还能解决优先级反转问题。

二、信号量例程

接下来,我将用相同的场景,用三个例程,介绍三个类型的信号量

1. 二值信号量例程

场景说明:二值信号量例程的和文章(二)的例程一致

定义结构体变量

#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "key.h"
#include "usart.h"#include "semphr.h"//信号量需引用此头文件SemaphoreHandle_t xBinarySemaphore;//定义二值信号量句柄

创建两个任务

void Task1(void *pvParams)// 任务1:按键检测,释放信号量(生产者)
{while(1){uint8_t KeyNum = Key_GetNum(0); // 获取按键状态printf("生产者准备释放信号量\r\n");if(KeyNum == 1)//按键按下{if(xSemaphoreGive(xBinarySemaphore) == pdTRUE)// 释放信号量(0->1){printf("信号量释放成功\r\n");}else{printf("信号量释放失败(当前已为1)\r\n");}}vTaskDelay(1000); // 1秒检测一次按键}
}
void Task2(void *pvParams)// 任务2:等待信号量,执行消费操作(消费者)
{while(1){// 等待信号量(1->0),永久阻塞直到获取到信号量xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);// 获取到信号量后执行消费操作printf("消费者接收到信号量,开始消费\r\n");vTaskDelay(500); // 模拟消费过程(500ms)printf("消费者消费完成\r\n");        }
}

main函数

int main(void)
{// 硬件初始化USART_Init();   Key_Init();     // 创建二值信号量(初始0)xBinarySemaphore = xSemaphoreCreateBinary();// 创建任务xTaskCreate(Task1, "Task1", 128, NULL, 2, NULL);  // 优先级2xTaskCreate(Task2, "Task2", 128, NULL, 1, NULL);  // 优先级1vTaskStartScheduler();return 0;
}

2.计数信号量例程

场景说明:计数信号量用于管理多个相同资源(如允许累计多次按键事件,消费者按顺序处理)。此处设置最大计数为 5(最多累计 5 次按键),初始计数为 0,每次按键释放信号量时计数 + 1,消费者每次获取信号量时计数 - 1。

定义结构体变量

#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "key.h"
#include "usart.h"#include "semphr.h"//信号量需引用此头文件SemaphoreHandle_t xCountingSemaphore;// 定义计数信号量句柄

创建两个任务

void Task1(void *pvParams)// 任务1:按键检测,释放计数信号量(累计事件)
{while(1){uint8_t KeyNum = Key_GetNum(0);printf("生产者准备释放计数信号量\r\n");if(KeyNum == 1){// 释放信号量(计数加多一个,最大不超过5)if(xSemaphoreGive(xCountingSemaphore) == pdTRUE){// 获取当前信号量计数(调试用)UBaseType_t uxCount = uxSemaphoreGetCount(xCountingSemaphore);printf("信号量释放成功,当前计数: %u\r\n", uxCount);}else{printf("信号量释放失败(已达最大计数5)\r\n");}}vTaskDelay(1000); // 1秒检测一次按键}
}
void Task2(void *pvParams)// 任务2:等待计数信号量,逐个处理事件
{while(1){// 等待信号量(计数减少一个,若计数为0则阻塞)xSemaphoreTake(xCountingSemaphore, portMAX_DELAY);// 获取当前剩余计数(调试用)UBaseType_t uxCount = uxSemaphoreGetCount(xCountingSemaphore);printf("消费者接收到信号量,开始处理(剩余计数: %u)\r\n", uxCount);vTaskDelay(500); // 模拟处理过程printf("消费者处理完成\r\n");        }
}

main函数

int main(void)
{USART_Init();Key_Init();// 创建计数信号量(最大计数5,初始计数0)xCountingSemaphore = xSemaphoreCreateCounting(5, 0);// 创建任务(优先级:Task1=2,Task2=1)xTaskCreate(Task1, "Task1", 128, NULL, 2, NULL);xTaskCreate(Task2, "Task2", 128, NULL, 1, NULL);vTaskStartScheduler();return 0;
}

3.互斥信号量例程

场景说明:互斥信号量用于保护共享资源(如全局变量),防止多个任务同时访问。此处用全局变量g_key_press_count记录按键次数,Task1 更新变量,Task2 读取变量,通过互斥信号量保证访问互斥。

定义结构体变量

#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "key.h"
#include "usart.h"#include "semphr.h"//信号量需引用此头文件SemaphoreHandle_t xMutexSemaphore;// 定义互斥信号量句柄
uint32_t g_key_press_count = 0;// 共享资源:按键按下次数计数器

创建两个任务

void Task1(void *pvParams)// 任务1:按键检测,更新共享资源(需互斥保护)
{while(1){uint8_t KeyNum = Key_GetNum(0);printf("生产者准备访问共享资源\r\n");if(KeyNum == 1){// 获取互斥信号量(若被占用则阻塞,最长等待100ms)if(xSemaphoreTake(xMutexSemaphore, 100 / portTICK_PERIOD_MS) == pdTRUE){// 临界区:安全更新共享资源g_key_press_count++;printf("共享资源更新:按键次数 = %u\r\n", g_key_press_count);// 释放互斥信号量(必须由获取它的任务释放)xSemaphoreGive(xMutexSemaphore);}else{printf("获取互斥信号量失败(资源被占用)\r\n");}}vTaskDelay(1000);}
}
void Task2(void *pvParams)// 任务2:读取共享资源(需互斥保护)
{while(1){// 获取互斥信号量(永久阻塞直到获取)xSemaphoreTake(xMutexSemaphore, portMAX_DELAY);// 临界区:安全读取共享资源printf("消费者读取共享资源:当前按键次数 = %u\r\n", g_key_press_count);vTaskDelay(500); // 模拟处理// 释放互斥信号量xSemaphoreGive(xMutexSemaphore);printf("消费者释放互斥信号量\r\n");vTaskDelay(100); // 短暂延迟,让Task1有机会获取资源}
}

main函数

int main(void)
{USART_Init();Key_Init();// 创建互斥信号量(初始值为1,代表资源可用)xMutexSemaphore = xSemaphoreCreateMutex();// 创建任务(优先级:Task1=2,Task2=1)xTaskCreate(Task1, "Task1", 128, NULL, 2, NULL);xTaskCreate(Task2, "Task2", 128, NULL, 1, NULL);vTaskStartScheduler();return 0;
}

三、二值信号量与互斥信号量的关系

互斥信号量是特殊的二值信号量,二者都只有 “0” 和 “1” 两种状态,互斥信号量的设计目的主要是为了解决资源共享的问题。

二值信号量:用于任务间同步(事件通知),比如 “一个任务触发事件,另一个任务等待事件”。

互斥信号量:用于保护共享资源,确保同一时间只有一个任务访问资源(避免并发冲突)。

在互斥信号量例程中:

  • 共享资源就是全局变量g_key_press_count(记录按键次数)

  • Task1 需要更新这个变量,Task2 需要读取这个变量,两者都可能并发访问。

  • 互斥信号量通过xSemaphoreTake()xSemaphoreGive()“上锁” 和 “解锁”,确保同一时间只有一个任务能操作g_key_press_count(比如避免 Task1 更新到一半时,Task2 读取到不完整的数据)

仔细观察这两个例程:

二值信号量Task1任务与互斥信号量Task1任务对比

二值信号量Task1任务
互斥信号量Task1任务

可以明显看到,二值信号量只需释放信号量就行了,而互斥信号量需要先获取信号量,然后再释放信号量。互斥信号量的xSemaphoreTake函数与xSemaphoreGive函数中间的临界区保护了全局变量的g_key_press_count。

二值信号量Task2任务与互斥信号量Task2任务对比

二值信号量Task2任务
互斥信号量Task2任务

同理,互斥信号量的xSemaphoreTake函数与xSemaphoreGive函数中间的临界区保护了printf打印的全局变量g_key_press_count。

二值信号量的初始值与互斥信号量的初始值

值得注意,二值信号量的初始值是0,互斥信号量的初始值是1。

四、二值信号量与互斥信号量总结

互斥信号量除了能保护共享资源,还能解决二值信号量不能解决的优先级反转的问题(后续文章会介绍优先级反转)。

如何选择二值信号量或者互斥信号量?

若需要传递事件(如 “按键按下通知处理任务”“中断完成通知应用任务”),用二值信号量,它更轻量,无所有权限制。

若需要保护共享资源(如全局变量、硬件寄存器、文件),用互斥信号量,它通过所有权和优先级继承机制确保资源安全访问。

结合例程来看:二值信号量例程的核心是 “按键事件同步”,互斥信号量例程的核心是 “按键计数变量的安全访问”

http://www.dtcms.com/a/450563.html

相关文章:

  • 欧洲网站服务器织梦网站更改网站的导航
  • 怎么搭建mysql数据库网站网站做多语言
  • 网站开发带后台南通网站建设心得
  • 福田做棋牌网站建设多少钱如何开发微信小程序商店
  • 运营网站是多少沧州开发网站多少钱
  • 泰州模板建站哪家好海外网站有哪些
  • 自主建设网站的意义软件园做网站
  • Linux内核进程管理子系统有什么第六十三回 —— 进程主结构详解(59)
  • 辽宁平台网站建设平台做网站引流做什么类型的网站最好
  • 企业网站推广注意事项做推广有什么好网站
  • 2018年网站建设培训会发言地方网站
  • 应用最广网站建设技术响应式购物网站模板
  • 大连庄河网站建设漯河网页设计
  • 嫦娥奔月庆中秋~Python语言实现
  • 内蒙古赤峰市建设局网站青海企业网站开发定制
  • 算法-试填法
  • 云建站步骤国内人做韩国网站一般都卖什么东西
  • 营销软件网站七星彩网站开发公司
  • 网站如何做问卷调查微信公众号微网站制作
  • 陕西煤业化工建设集团有限公司网站微网站开发怎么写
  • 建设网站需要什么样的服务器广州网站开发招聘信息
  • 营销型网站建设ppt模板下载百度在全国有哪些代理商
  • 网站建设 招聘国外有哪些网站做推广的比较好
  • UNIX下C语言编程与实践35-UNIX 守护进程编写:后台执行、脱离终端、清除掩码与信号处理
  • 漳州做网站优化全网有哪些网站可以做淘客
  • 晋江网站建设山东省建设局拖欠工资网站
  • 命令行创建https证书详解
  • Linux 基础命令的 7 大核心模块
  • 沐川移动网站建设设计网站私单价格
  • 杭州网站建设 博客南京做网站品牌