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

信号量本质 信号量实验(控制车辆运行,优先级反转)互斥量

信号量本质

 前面介绍的队列(queue)可以用于传输数据:在任务之间、任务和中断之间。

消息队列用于传输多个数据,但是有时候我们只需要传递状态,这个状态值需要用一个
数值表示,比如:
卖家:做好了 1 个包子!做好了 2 个包子!做好了 3 个包子!
买家:买了 1 个包子,包子数量减 1
这个停车位我占了,停车位减 1
我开车走了,停车位加 1
在这种情况下我们只需要维护一个数值,使用信号量效率更高、更节省内存

信号量的特性

信号量的常规操作

信号量这个名字很恰当:
信号:起通知作用
量:还可以用来表示资源的数量
" " 没有限制时,它就是 " 计数型信号量 "(Counting Semaphores)
" " 只有 0 1 两个取值时,它就是 " 二进制信号量 "(Binary Semaphores)
支持的动作: "give" 给出资源,计数值加 1 "take" 获得资源,计数值减 1
计数型信号量的典型场景是:
计数:事件产生时 "give" 信号量,让计数值加 1 ;处理事件时要先 "take" 信号
量,就是获得信号量,让计数值减 1
资源管理:要想访问资源需要先 "take" 信号量,让计数值减 1 ;用完资源后
"give" 信号量,让计数值加 1
信号量的"give"、"take"双方并不需要相同,可以用于生产者-消费者场合:
生产者为任务 A B ,消费者为任务 C D
153
一开始信号量的计数值为 0 ,如果任务 C D 想获得信号量,会有两种结
果:
阻塞:买不到东西咱就等等吧,可以定个闹钟 ( 超时时间 )
即刻返回失败:不等
任务 A B 可以生产资源,就是让信号量的计数值增加 1 ,并且把等待这个
资源的顾客唤醒
唤醒谁?谁优先级高就唤醒谁,如果大家优先级一样就唤醒等待时间最长的

信号量跟队列的对比

两种信号量的对比 

信号量的计数值都有限制:限定了最大值。如果最大值被限定为 1,那么它就是二进制
信号量;如果最大值不是 1,它就是计数型信号量。

信号量函数

使用信号量时,先创建、然后去添加资源、获得资源。使用句柄来表示一个信号量。

创建

二进制信号量

xSemaphoreCreateBinary() 是 FreeRTOS 中用于创建二进制信号量的函数。二进制信号量是一个非常有用的同步机制,适用于任务之间的协调与互斥访问。

函数原型
SemaphoreHandle_t xSemaphoreCreateBinary(void);
返回值
  • 成功:返回一个有效的信号量句柄 (SemaphoreHandle_t)。
  • 失败:返回 NULL,这通常表示系统内存不足或信号量创建失败。
使用场景
  • 任务同步:一个任务可以在完成某个操作后,释放信号量,通知其他任务可以继续执行。
  • 互斥访问:确保同一时刻只有一个任务可以访问共享资源,以防数据竞争和不一致性。
计数型信号量

xSemaphoreCreateCounting() 是 FreeRTOS 中用于创建计数信号量的函数。计数信号量可以用于限制对共享资源的访问,支持多个任务同时访问,并且能够管理多个资源的可用数量。

函数原型
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
参数
  • uxMaxCount:信号量的最大计数值,表示可以同时获得的最大资源数量。
  • uxInitialCount:信号量的初始计数值,表示在创建时可用的资源数量。
返回值
  • 成功:返回一个有效的信号量句柄 (SemaphoreHandle_t)。
  • 失败:返回 NULL,通常由于内存不足或系统限制。
使用场景
  • 资源池管理:当你有一个固定数量的资源(如线程池、连接池等)时,可以使用计数信号量来管理它们的分配和释放。
  • 任务同步:在多个任务间同步执行,允许一定数量的任务并行访问某些资源。

删除

对于动态创建的信号量,不再需要它们时,可以删除它们以回收内存。
vSemaphoreDelete 可以用来删除二进制信号量、计数型信号量,函数原型如下
函数原型
void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);
参数
  • xSemaphore:要删除的信号量的句柄,类型为 SemaphoreHandle_t
注意事项
  1. 信号量状态:在调用 vSemaphoreDelete() 之前,确保没有任务正在使用该信号量。否则,可能会导致未定义的行为。

  2. 内存管理:使用 vSemaphoreDelete() 后,信号量的句柄将变为无效。后续尝试使用这个句柄(如调用 xSemaphoreTake()xSemaphoreGive())将会引发错误。

  3. 任务优先级:如果信号量被用在多个任务中,确保在所有任务完成其对信号量的使用后再进行删除。

give/take

give

xSemaphoreGive() 是 FreeRTOS 中用于释放信号量的函数。它的主要功能是将信号量的计数值增加,允许其他任务获取信号量。

函数原型
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
参数
  • xSemaphore:要释放的信号量的句柄,类型为 SemaphoreHandle_t
返回值
  • pdTRUE:成功释放信号量。
  • pdFALSE:释放信号量失败(通常在信号量未被占用时调用时会失败)。
使用场景
  • 任务同步:在多任务环境中,当一个任务完成了对某个共享资源的使用后,可以调用 xSemaphoreGive() 释放信号量,允许其他任务访问该资源。
  • 计数信号量:在计数信号量中,调用 xSemaphoreGive() 将信号量的计数增加,可以表示资源的可用数量。
take

xSemaphoreTake() 是 FreeRTOS 中用于获取信号量的函数。它的主要作用是尝试获得一个信号量,如果信号量可用,函数将成功返回,并将信号量的计数减一;如果信号量不可用,函数将根据设置的等待时间进行阻塞或立即返回失败。

函数原型
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
参数
  • xSemaphore:要获取的信号量的句柄,类型为 SemaphoreHandle_t
  • xTicksToWait:等待信号量可用的时间,以滴答数为单位。如果设置为 portMAX_DELAY,则任务将无限期阻塞,直到获得信号量。
返回值
  • pdTRUE:成功获取信号量。
  • pdFALSE:获取信号量失败(在设定的时间内没有获得信号量)。
使用场景
  • 任务同步:在多任务系统中,一个任务可能需要在访问共享资源之前先获取信号量,确保其他任务不会同时访问该资源。
  • 保护共享资源:通过信号量的机制,可以防止数据竞争和资源冲突。

信号量实验

控制车辆运行

static void CarTask(void *params)
{struct car *pcar = params;struct ir_data idata;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);xSemaphoreTake(g_xSemTicks,portMAX_DELAY);while (1){/* 读取按键值:读队列 *///xQueueReceive(xQueueIR, &idata, portMAX_DELAY);/* 控制汽车往右移动 *///if (idata.val == pcar->control_key){if (pcar->x < g_xres - CAR_LENGTH){/* 隐藏汽车 */HideCar(pcar);/* 调整位置 */pcar->x +=10 ;if (pcar->x > g_xres - CAR_LENGTH){pcar->x = g_xres - CAR_LENGTH;}/* 重新显示汽车 */ShowCar(pcar);vTaskDelay(50);if(pcar->x==g_xres-CAR_LENGTH){xSemaphoreGive(g_xSemTicks);vTaskDelete(NULL);}}}}
}void car_game(void)
{int x;int i, j;g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);draw_init();draw_end();g_xSemTicks  =xSemaphoreCreateBinary();xSemaphoreGive(g_xSemTicks);xSemaphoreGive(g_xSemTicks);xSemaphoreGive(g_xSemTicks);/* 画出路标 */for (i = 0; i < 3; i++){for (j = 0; j < 8; j++){draw_bitmap(16*j, 16+17*i, roadMarking, 8, 1, NOINVERT, 0);draw_flushArea(16*j, 16+17*i, 8, 1);}}/* 创建3个汽车任务 */
#if 0	for (i = 0; i < 3; i++){draw_bitmap(g_cars[i].x, g_cars[i].y, carImg, 15, 16, NOINVERT, 0);draw_flushArea(g_cars[i].x, g_cars[i].y, 15, 16);}
#endifxTaskCreate(CarTask, "car1", 128, &g_cars[0], osPriorityNormal, NULL);xTaskCreate(CarTask, "car2", 128, &g_cars[1], osPriorityNormal, NULL);xTaskCreate(CarTask, "car3", 128, &g_cars[2], osPriorityNormal, NULL);	
}

优先级反转

static void CarTask1(void *params)
{struct car *pcar = params;struct ir_data idata;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);xSemaphoreTake(g_xSemTicks,portMAX_DELAY);while (1){/* 读取按键值:读队列 *///xQueueReceive(xQueueIR, &idata, portMAX_DELAY);/* 控制汽车往右移动 *///if (idata.val == pcar->control_key){if (pcar->x < g_xres - CAR_LENGTH){/* 隐藏汽车 */HideCar(pcar);/* 调整位置 */pcar->x +=5 ;if (pcar->x > g_xres - CAR_LENGTH){pcar->x = g_xres - CAR_LENGTH;}/* 重新显示汽车 */ShowCar(pcar);vTaskDelay(50);if(pcar->x==g_xres-CAR_LENGTH){xSemaphoreGive(g_xSemTicks);vTaskDelete(NULL);}}}}
}
static void CarTask2(void *params)
{struct car *pcar = params;struct ir_data idata;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);vTaskDelay(1000);//xSemaphoreTake(g_xSemTicks,portMAX_DELAY);while (1){/* 读取按键值:读队列 *///xQueueReceive(xQueueIR, &idata, portMAX_DELAY);/* 控制汽车往右移动 *///if (idata.val == pcar->control_key){if (pcar->x < g_xres - CAR_LENGTH){/* 隐藏汽车 */HideCar(pcar);/* 调整位置 */pcar->x +=4 ;if (pcar->x > g_xres - CAR_LENGTH){pcar->x = g_xres - CAR_LENGTH;}/* 重新显示汽车 */ShowCar(pcar);mdelay(50);if(pcar->x==g_xres-CAR_LENGTH){//xSemaphoreGive(g_xSemTicks);//vTaskDelete(NULL);}}}}
}
static void CarTask3(void *params)
{struct car *pcar = params;struct ir_data idata;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);vTaskDelay(2000);xSemaphoreTake(g_xSemTicks,portMAX_DELAY);while (1){/* 读取按键值:读队列 *///xQueueReceive(xQueueIR, &idata, portMAX_DELAY);/* 控制汽车往右移动 *///if (idata.val == pcar->control_key){if (pcar->x < g_xres - CAR_LENGTH){/* 隐藏汽车 */HideCar(pcar);/* 调整位置 */pcar->x +=4 ;if (pcar->x > g_xres - CAR_LENGTH){pcar->x = g_xres - CAR_LENGTH;}/* 重新显示汽车 */ShowCar(pcar);vTaskDelay(50);if(pcar->x==g_xres-CAR_LENGTH){xSemaphoreGive(g_xSemTicks);vTaskDelete(NULL);}}}}
}void car_game(void)
{int x;int i, j;g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);draw_init();draw_end();g_xSemTicks  =xSemaphoreCreateBinary();xSemaphoreGive(g_xSemTicks);xSemaphoreGive(g_xSemTicks);xSemaphoreGive(g_xSemTicks);/* 画出路标 */for (i = 0; i < 3; i++){for (j = 0; j < 8; j++){draw_bitmap(16*j, 16+17*i, roadMarking, 8, 1, NOINVERT, 0);draw_flushArea(16*j, 16+17*i, 8, 1);}}/* 创建3个汽车任务 */
#if 0	for (i = 0; i < 3; i++){draw_bitmap(g_cars[i].x, g_cars[i].y, carImg, 15, 16, NOINVERT, 0);draw_flushArea(g_cars[i].x, g_cars[i].y, 15, 16);}
#endifxTaskCreate(CarTask1, "car1", 128, &g_cars[0], osPriorityNormal, NULL);xTaskCreate(CarTask2, "car2", 128, &g_cars[1], osPriorityNormal+2, NULL);xTaskCreate(CarTask3 , "car3", 128, &g_cars[2], osPriorityNormal+3 , NULL);	
}

互斥量

互斥量(Mutex,Mutual Exclusion)是一种用于同步的机制,主要用于控制对共享资源的访问,确保同一时间只有一个线程或任务能够访问该资源。这在多线程或多任务环境中非常重要,以避免数据竞争和不一致的问题。

互斥量(Mutex)是用于保护共享资源的一种同步机制,通常用于防止多个任务同时访问同一个资源,从而避免数据竞争和不一致性。在 FreeRTOS 中,互斥量是一种特殊类型的信号量,它提供了更强的排他性。

互斥量的特点

  1. 独占性:一次只有一个任务可以拥有互斥量。如果一个任务已经获得了互斥量,其他任务必须等待,直到该互斥量被释放。
  2. 优先级继承:如果一个低优先级任务持有互斥量,而一个高优先级任务试图获取该互斥量,低优先级任务的优先级会暂时提升到高优先级任务的级别,从而减少优先级反转的风险。
  3. 适用于保护共享资源:互斥量常用于保护共享数据或资源,以确保数据的完整性。

互斥量的基本工作原理:

  1. 获取和释放

    • 当一个任务需要访问共享资源时,它会尝试获取互斥量。
    • 如果互斥量当前未被其他任务占用,任务成功获取互斥量并可以安全地访问资源。
    • 如果互斥量已被其他任务占用,当前任务会被阻塞,直到互斥量被释放。
  2. 释放互斥量

    • 当任务完成对资源的操作后,它会释放互斥量,使其他被阻塞的任务能够获取该互斥量并访问资源。

使用互斥量的优势:

  • 数据一致性:通过确保只有一个任务在任何时刻访问共享资源,互斥量可以避免数据冲突和不一致性。
  • 避免死锁:如果设计得当,互斥量可以减少或避免死锁情况的发生。

注意事项:

  • 性能开销:频繁地获取和释放互斥量可能会导致性能下降,因此在设计时要考虑优化。
  • 死锁风险:如果多个任务相互等待对方释放互斥量,可能会导致系统死锁。


http://www.mrgr.cn/news/64267.html

相关文章:

  • Python List列表
  • promise的catch放在then前面的场景
  • LeetCode994. 腐烂的橘子(2024秋季每日一题 54)
  • 快速入门:Visual Studio 中的 Docker
  • java开发等一些问题,持续更新
  • 瑞格智慧心理服务平台 NPreenSMSList.asmx sql注入漏洞复现
  • Java基于SpringBoot+Vue框架的房屋租赁管理系统(附源码,文档)
  • Nuxt.js 应用中的 nitro:config 事件钩子详解
  • (JVM)全面深入Java GC!!带你完全了解 Java 的‘ 灵魂 ‘ GC垃圾回收机制!!
  • 自制esp32开发板,wifi和蓝牙工作不正常一例
  • 【多态】析构函数的重写
  • 字符串统计(Python)
  • 2024 年江西省职业院校技能大赛应用软件系统开发赛项竞赛方案(高职组)
  • Nop入门:极简服务层实现
  • untiy mlagents 飞机大战 ai训练
  • Java--正则表达式入门指南
  • ros入门:使用c++打印hello world
  • try…catch…finally语句里return语句的执行顺序是怎样的?
  • 【北京迅为】《STM32MP157开发板嵌入式开发指南》-第七十章 Buildroot制作根文件系统
  • 约瑟夫环问题——4个解法总结(C语言)
  • HTMLCSS:旋转的动态卡片
  • LInux系统编程(二)操作系统和进程
  • 锁策略, cas 和 synchronized 优化过程
  • Python爬虫详解:原理、常用库与实战案例
  • 【刷题13】链表专题
  • 使用WebAssembly优化Web应用性能