news 2026/4/29 16:05:25

信号量(二进制/计数)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
信号量(二进制/计数)

二进制信号量定义:

#include"semphr.h"// SemaphoreHandle_t// 二进制信号量(Binary Semaphore)SemaphoreHandle_t xBinarySemaphore;voidvTaskA(void*pvParameters){while(1){// 获取信号量if(xSemaphoreTake(xBinarySemaphore,portMAX_DELAY)==pdTRUE){printf("TaskA: Got semaphore, doing work...\r\n");vTaskDelay(500/portTICK_PERIOD_MS);xSemaphoreGive(xBinarySemaphore);// 释放}}}

使用示例:

/** * @brief 主应用函数:二进制信号量 * * @note None * @param None * @return None * @warning 此函数不会返回,内部包含无限循环 * @remark 通过函数指针从main()调用,支持灵活的启动架构 */voidCurrent_Program4(void){LED_Init();// 初始化LED设备UART1_Config();// UART1串口初始化xBinarySemaphore=xSemaphoreCreateBinary();if(xBinarySemaphore==NULL){// 创建失败(堆内存不足)}// 创建后信号量不可用(值为0),通常需要先 Give 一次xSemaphoreGive(xBinarySemaphore);// 使其变为可用//xSemaphoreGive(xBinarySemaphore); // 释放信号量xTaskCreate(vTaskA,"vTaskA",128,NULL,1,NULL);// 启动调度器(永远不会返回)vTaskStartScheduler();// 理论上程序不会执行到这里,但为了安全,可以加一个死循环while(1){}}

下面详细解释二进制信号量(Binary Semaphore)代码所涉及的核心知识点。二进制信号量是 FreeRTOS 中用于任务同步资源共享控制的轻量级机制,可以把它看作一个只能取 0 或 1 的计数器(0 表示不可用,1 表示可用)。


一、二进制信号量的本质

  • 计数范围:0 或 1。
  • 操作
    • Take(获取):如果信号量值为 1,则将其减为 0 并立即返回成功;如果为 0,则任务可阻塞等待。
    • Give(释放):将信号量值从 0 变为 1(若已有任务等待该信号量,则唤醒其中一个)。
  • 与队列的区别:队列可以携带数据,而信号量只传递“事件发生”或“资源可用”这一布尔信息,没有数据负载。

二、代码中涉及的 API 函数

1.xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait)

  • 作用:获取(消耗)一个信号量。
  • 参数
    • xSemaphore:信号量句柄。
    • xTicksToWait:若信号量不可用,最多等待多少个 tick。portMAX_DELAY表示无限等待。
  • 返回值
    • pdTRUE:成功获取信号量。
    • pdFALSE:超时仍未获取到。
  • 注意:在中断服务函数中应使用xSemaphoreTakeFromISR()

2.xSemaphoreGive(SemaphoreHandle_t xSemaphore)

  • 作用:释放(给出)一个信号量,使其值从 0 变为 1。
  • 参数:信号量句柄。
  • 返回值
    • pdTRUE:释放成功。
    • pdFALSE:释放失败(例如信号量已经是 1,但一般不会失败)。
  • 注意:在中断中应使用xSemaphoreGiveFromISR()

三、代码运行流程分析

SemaphoreHandle_t xBinarySemaphore;// 全局句柄voidvTaskA(void*pvParameters){while(1){// 1. 尝试获取信号量(若为 0 则阻塞)if(xSemaphoreTake(xBinarySemaphore,portMAX_DELAY)==pdTRUE){printf("TaskA: Got semaphore, doing work...\r\n");// 2. 模拟占用资源的工作(持有信号量期间)vTaskDelay(500/portTICK_PERIOD_MS);// 3. 工作完成,释放信号量xSemaphoreGive(xBinarySemaphore);}}}

执行逻辑

  • 假设初始信号量值为 1(可用)。
  • 任务 A 调用xSemaphoreTake成功,将信号量减为 0,然后打印并延时 500ms。
  • 在延时期间,任务 A 仍然持有信号量(信号量值为 0),其他任何想获取该信号量的任务都会进入阻塞状态。
  • 延时结束后,任务 A 调用xSemaphoreGive,信号量恢复为 1。此时如果有其他任务正在等待该信号量,则会被唤醒并获得信号量。

四、典型应用场景

1. 任务同步(Task Synchronization)

一个任务给出信号量,另一个任务获取它,表示“事件已发生”。

  • 例如:中断服务程序给出信号量,后台任务获取后处理数据。

2. 资源互斥(Resource Mutex)——有缺陷

上述代码将二进制信号量用于互斥访问共享资源(如打印机、缓冲区)。但二进制信号量不提供优先级继承,可能导致优先级反转问题。对于互斥,FreeRTOS 专门提供了xMutex(互斥量),支持优先级继承。

3. 单次通知

如果信号量初始为 0,一个任务Give,另一个任务Take,实现一次性通知。


五、二进制信号量 vs 互斥量

特性二进制信号量互斥量(Mutex)
用途同步、简单标志互斥访问共享资源
优先级继承❌ 不支持✅ 支持(防止优先级反转)
谁可以释放任何任务/中断只能由获取它的任务释放
递归获取❌ 不能重复获取✅ 支持(同一个任务可多次获取,需释放相同次数)
创建函数xSemaphoreCreateBinary()xSemaphoreCreateMutex()

因此:如果vTaskA中的代码是在保护共享资源(如写一个全局变量),应使用互斥量而非二进制信号量。若只是用作任务间“干完活通知我”之类的一次性同步,二进制信号量就已足够。


六、注意事项

  1. 初始状态

    • xSemaphoreCreateBinary()创建的信号量初始值为 0。如果希望一开始就可用,创建后需要先调用一次xSemaphoreGive()
    • xSemaphoreCreateMutex()创建的互斥量初始值为 1(可用)。
  2. 死锁风险

    • 如果获取信号量的任务在释放前被无限期挂起(如永循环),其他所有等待该信号量的任务都将永远阻塞。
  3. 中断中使用

    • 中断服务程序中只能使用xSemaphoreGiveFromISR(),并且不能阻塞(不能调用Take带超时)。
    • 示例给出信号量后,应检查pxHigherPriorityTaskWoken并做任务切换。
  4. 所有权

    • 二进制信号量没有“所有者”概念,任何任务(包括中断)都可以释放它,这容易造成意外释放。互斥量则只有持有者才能释放,更安全。
  5. 递归获取

    • 二进制信号量不支持递归获取。同一个任务再次Take同一个二进制信号量会阻塞(除非先Give)。而互斥量支持递归。

七、完善示例:正确的互斥使用(改用互斥量)

SemaphoreHandle_t xMutex;voidvTaskA(void*pvParameters){while(1){if(xSemaphoreTake(xMutex,portMAX_DELAY)==pdTRUE){// 临界区:访问共享资源printf("TaskA: Accessing shared resource...\r\n");vTaskDelay(500/portTICK_PERIOD_MS);xSemaphoreGive(xMutex);}}}

创建:

xMutex=xSemaphoreCreateMutex();

八、总结

  • 二进制信号量是 FreeRTOS 中最简单的同步工具,状态只有 0/1。
  • Take减少计数,Give增加计数。
  • 适用于任务间同步(如中断通知任务)和简单的资源控制(注意优先级反转风险)。
  • 对于保护共享资源的互斥场景,应优先使用xSemaphoreCreateMutex()创建的互斥量。

你给出的代码展示了典型的信号量获取与释放模式,只要理解其适用边界,就能灵活运用。

附录

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 16:01:22

如何5分钟上手QtScrcpy:Android设备跨平台投屏与键鼠控制完全指南

如何5分钟上手QtScrcpy:Android设备跨平台投屏与键鼠控制完全指南 【免费下载链接】QtScrcpy Android实时投屏软件,此应用程序提供USB(或通过TCP/IP)连接的Android设备的显示和控制。它不需要任何root访问权限 项目地址: https://gitcode.com/barry-ra…

作者头像 李华
网站建设 2026/4/29 16:01:06

AI专著撰写秘籍!20万字专著一键生成,多款AI工具助力高效写作

学术专著写作难题与AI工具解决方案 对于绝大多数学术工作者而言,撰写学术专著面临的主要难题,便是“有限精力”与“无尽需求”之间的矛盾。创作一本专著通常需要三到五年,甚至更长的时间,而研究者在日常生活中还必须承担教学、科…

作者头像 李华
网站建设 2026/4/29 15:56:50

Flowframes视频插帧工具:基于AI的帧率提升技术实现与应用

Flowframes视频插帧工具:基于AI的帧率提升技术实现与应用 【免费下载链接】flowframes Flowframes Windows GUI for video interpolation using DAIN (NCNN) or RIFE (CUDA/NCNN) 项目地址: https://gitcode.com/gh_mirrors/fl/flowframes 在视频处理领域&am…

作者头像 李华