首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >内核中任务信号量和任务队列的设计哲学

内核中任务信号量和任务队列的设计哲学

作者头像
一个平凡而乐于分享的小比特
发布2026-02-02 16:03:29
发布2026-02-02 16:03:29
1120
举报

🔥作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生,研究方向无线联邦学习 🎬擅长领域:驱动开发,嵌入式软件开发,BSP开发 ❄️作者主页:一个平凡而乐于分享的小比特的个人主页 ✨收录专栏:UCOS-III,本专栏为UCOS-III学习记录 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖

内核中任务信号量和任务队列的设计哲学

任务信号量start_task函数:

代码语言:javascript
复制
void start_task(void *p_arg)              
{
    OS_ERR err;
    CPU_INT32U  cnts = 0;
    CPU_Init();
    CPU_SR_ALLOC();
    
    cnts = HAL_RCC_GetSysClockFreq() / OS_CFG_TICK_RATE_HZ;
    OS_CPU_SysTickInit(cnts);
    
    

    CPU_CRITICAL_ENTER();           /* 进入临界区 */
    /* 创建task1 */
    task1_stack = mymalloc(SRAMIN,TASK1_STACK_SIZE * sizeof(CPU_STK));
    OSTaskCreate (  (OS_TCB*     )  &task1_tcb,
                    (CPU_CHAR*   )  "task1",
                    (OS_TASK_PTR )  task1,
                    (void*       )  0,
                    (OS_PRIO     )  TASK1_PRIO,
                    (CPU_STK*    )  task1_stack,
                    (CPU_STK_SIZE)  TASK1_STACK_SIZE / 10,
                    (CPU_STK_SIZE)  TASK1_STACK_SIZE,
                    (OS_MSG_QTY  )  0,
                    (OS_TICK     )  0,
                    (void*       )  0,
                    (OS_OPT      )  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                    (OS_ERR*     )  &err);
                    
    /* 创建task2 */
    task2_stack = mymalloc(SRAMIN,TASK2_STACK_SIZE * sizeof(CPU_STK));
    OSTaskCreate (  (OS_TCB*     )  &task2_tcb,
                    (CPU_CHAR*   )  "task2",
                    (OS_TASK_PTR )  task2,
                    (void*       )  0,
                    (OS_PRIO     )  TASK2_PRIO,
                    (CPU_STK*    )  task2_stack,
                    (CPU_STK_SIZE)  TASK2_STACK_SIZE / 10,
                    (CPU_STK_SIZE)  TASK2_STACK_SIZE,
                    (OS_MSG_QTY  )  0,
                    (OS_TICK     )  0,
                    (void*       )  0,
                    (OS_OPT      )  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                    (OS_ERR*     )  &err);
    
    OSTaskSemSet (&task2_tcb , 1 , &err);     //<---这里
    CPU_CRITICAL_EXIT();            /* 退出临界区 */
    OSTaskDel((OS_TCB *)0, &err);
}
/* 往task2释放任务信号量 */
void task1(void *p_arg) 
{
    OS_ERR  err;
    uint8_t key = 0;
    while(1)
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            printf("释放任务信号量!!\r\n");
            OSTaskSemPost (&task2_tcb , OS_OPT_POST_NONE , &err);
        }
        OSTimeDly (10 , OS_OPT_TIME_DLY , &err);
    }
}  

/* 获取任务信号量 */
void task2(void *p_arg) 
{
    OS_ERR  err;
    OS_SEM_CTR cnt = 0;
    while(1)
    {
        cnt = OSTaskSemPend (0 , OS_OPT_PEND_BLOCKING , 0 , &err);
        printf("获取任务信号量成功!,cnt的值为:%d\r\n",cnt);
        OSTimeDly (1000 , OS_OPT_TIME_DLY , &err);
    }
}  

任务队列start_task函数:

代码语言:javascript
复制
void start_task(void *p_arg)              
{
    OS_ERR err;
    CPU_INT32U  cnts = 0;
    CPU_Init();
    CPU_SR_ALLOC();
    
    cnts = HAL_RCC_GetSysClockFreq() / OS_CFG_TICK_RATE_HZ;
    OS_CPU_SysTickInit(cnts);
    
    
    CPU_CRITICAL_ENTER();           /* 进入临界区 */
    /* 创建task1 */
    task1_stack = mymalloc(SRAMIN,TASK1_STACK_SIZE * sizeof(CPU_STK));
    OSTaskCreate (  (OS_TCB*     )  &task1_tcb,
                    (CPU_CHAR*   )  "task1",
                    (OS_TASK_PTR )  task1,
                    (void*       )  0,
                    (OS_PRIO     )  TASK1_PRIO,
                    (CPU_STK*    )  task1_stack,
                    (CPU_STK_SIZE)  TASK1_STACK_SIZE / 10,
                    (CPU_STK_SIZE)  TASK1_STACK_SIZE,
                    (OS_MSG_QTY  )  0,
                    (OS_TICK     )  0,
                    (void*       )  0,
                    (OS_OPT      )  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                    (OS_ERR*     )  &err);
                    
    /* 创建task2 */
    task2_stack = mymalloc(SRAMIN,TASK2_STACK_SIZE * sizeof(CPU_STK));
    OSTaskCreate (  (OS_TCB*     )  &task2_tcb,
                    (CPU_CHAR*   )  "task2",
                    (OS_TASK_PTR )  task2,
                    (void*       )  0,
                    (OS_PRIO     )  TASK2_PRIO,
                    (CPU_STK*    )  task2_stack,
                    (CPU_STK_SIZE)  TASK2_STACK_SIZE / 10,
                    (CPU_STK_SIZE)  TASK2_STACK_SIZE,
                    (OS_MSG_QTY  )  0,
                    (OS_TICK     )  0,
                    (void*       )  0,
                    (OS_OPT      )  (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                    (OS_ERR*     )  &err);
                    
    CPU_CRITICAL_EXIT();            /* 退出临界区 */
    OSTaskDel((OS_TCB *)0, &err);
}
/* 写任务内嵌队列 */
void task1(void *p_arg) 
{
    OS_ERR  err;
    uint8_t key = 0;
    while(1)
    {
        key = key_scan(0);
        if(key == KEY0_PRES || key == KEY1_PRES || key == KEY2_PRES || key == WKUP_PRES)
        {
            printf("发送键值!\r\n");
            OSTaskQPost (&task2_tcb , &key , sizeof(key) , OS_OPT_POST_FIFO , &err);
        }
        OSTimeDly(10,OS_OPT_TIME_DLY,&err);
        
    }
}  

/* 读任务内嵌队列 */
void task2(void *p_arg) 
{
    OS_ERR  err;
    uint8_t * key;
    OS_MSG_SIZE size = 0;
    while(1)
    {
        key = OSTaskQPend (0 , OS_OPT_PEND_BLOCKING , &size , 0 , &err);       
        printf("接收到的键值为:%d\r\n", *key);
        printf("接收到数据长度为:%d字节\r\n", size);
    }
}  

疑问1:为什么任务信号量start_task函数,需要用到OSTaskSemSet (&task2_tcb , 1 , &err)代码指定task2_tcb接收信号量,在task1中不是用OSTaskSemPost (&task2_tcb , OS_OPT_POST_NONE , &err)也指定了吗?

疑问2:为什么任务队列start_task函数,不需要用到OSTaskSemSet (&task2_tcb , 1 , &err)类似的函数指定task2_tcb接收队列数据呢

针对疑问,我去deepseek了一下,感觉讲的挺深入的,分享给大家

核心答案:因为设计理念不同

任务信号量:需要明确初始状态
代码语言:javascript
复制
// 任务信号量需要明确初始值,因为:
// - 可能是二值信号量:初始值=0或1
// - 可能是计数信号量:初始值=N
// - 操作系统不知道你的用途
OSTaskSemSet(&task2_tcb, 1, &err);  // 必须明确设置初始状态
任务消息队列:初始状态是确定的
代码语言:javascript
复制
// 任务消息队列的初始状态永远是:
// - 空队列
// - 没有消息
// - 等待接收状态
// 不需要额外设置,因为初始状态只有这一种可能!

实际场景对比

任务信号量的不同用途:
代码语言:javascript
复制
// 用例1:二值信号量(互斥)
OSTaskSemSet(&task_tcb, 1, &err);  // 初始可用

// 用例2:事件计数  
OSTaskSemSet(&task_tcb, 0, &err);  // 初始无事件

// 用例3:资源池
OSTaskSemSet(&task_tcb, 5, &err);  // 初始5个资源
// ↑ 必须根据用途设置不同的初始值
任务消息队列的唯一用途:
代码语言:javascript
复制
// 所有用例都一样:
// - 初始队列为空
// - 等待消息到达
// ↑ 不需要设置,因为初始状态都一样

内核代码深度剖析

1. 在 OSTaskCreate() 函数内部
代码语言:javascript
复制
void OSTaskCreate (OS_TCB        *p_tcb,
                   CPU_CHAR      *p_name,
                   OS_TASK_PTR    p_task,
                   void          *p_arg,
                   OS_PRIO        prio,
                   CPU_STK       *p_stk_base,
                   CPU_STK_SIZE   stk_limit,
                   CPU_STK_SIZE   stk_size,
                   OS_MSG_QTY     q_size,
                   OS_TICK        time_quanta,
                   void          *p_ext,
                   OS_OPT         opt,
                   OS_ERR        *p_err)
{
    // ... 参数检查
    
    /* 初始化TCB - 这是关键步骤! */
    OS_TaskInitTCB(p_tcb);    // ← 在这里初始化TCB的所有成员
    
    // ... 其他初始化代码
}
2. OS_TaskInitTCB() 函数的详细实现
代码语言:javascript
复制
void  OS_TaskInitTCB (OS_TCB  *p_tcb)
{
    /* 清零整个TCB结构体 */
    memset((void*)p_tcb, 0, sizeof(OS_TCB));  // ← 关键:全部置零!
    

    /* 然后设置一些特定的初始值 */
    p_tcb->TaskState    = OS_TASK_STATE_RDY;
    p_tcb->Prio         = OS_CFG_PRIO_MAX - 1;
    p_tcb->StkPtr       = DEF_NULL;
    p_tcb->StkSize      = 0;
    
    /* 初始化任务内嵌消息队列 */
    OS_MsgQInit(&p_tcb->MsgQ,         // ← 专门初始化消息队列
                (OS_MSG_QTY)0);
    
    /* 初始化其他内核对象 */
    OS_PendListInit(&p_tcb->PendList);
    
    // ... 设置其他成员

}
3. 消息队列的专门初始化函数
代码语言:javascript
复制
void  OS_MsgQInit (OS_MSG_Q    *p_msg_q,
                   OS_MSG_QTY   size)
{
    p_msg_q->InPtr      = (OS_MSG *)0;    // 输入指针置NULL
    p_msg_q->OutPtr     = (OS_MSG *)0;    // 输出指针置NULL  
    p_msg_q->NbrEntries = (OS_MSG_QTY)0;  // 条目数置0
    p_msg_q->NbrEntriesMax = (OS_MSG_QTY)0; // 最大条目数置0
}

完整的初始化流程

步骤1:内存分配
代码语言:javascript
复制
// 你的代码:
task2_stack = mymalloc(SRAMIN, TASK2_STACK_SIZE * sizeof(CPU_STK));
// 栈内存分配好了,但内容是随机的
步骤2:调用 OSTaskCreate()
代码语言:javascript
复制
OSTaskCreate(&task2_tcb, "task2", task2, ...);
步骤3:操作系统执行初始化
代码语言:javascript
复制
// 在OSTaskCreate()内部:
1. memset(&task2_tcb, 0, sizeof(OS_TCB));     // TCB全部清零
2. OS_MsgQInit(&task2_tcb->MsgQ, 0);          // 消息队列专门初始化
3. // 设置任务名、优先级、栈指针等
4. // 把任务加入到就绪表
步骤4:初始化完成后的TCB状态
代码语言:javascript
复制
task2_tcb.MsgQ.InPtr      = NULL      ← 已初始化
task2_tcb.MsgQ.OutPtr     = NULL      ← 已初始化  
task2_tcb.MsgQ.NbrEntries = 0         ← 已初始化
task2_tcb.SemCtr          = 0         ← 已清零,但这是随机清零的!

关键问题:为什么信号量还需要单独设置?

消息队列的初始化:
代码语言:javascript
复制
// 操作系统知道MsgQ是一个复杂结构体,需要专门初始化
OS_MsgQInit(&p_tcb->MsgQ, 0);
// 明确设置每个成员为合理的初始值
信号量计数器的初始化:
代码语言:javascript
复制
// 操作系统只是简单地把整个TCB清零
memset(p_tcb, 0, sizeof(OS_TCB));
// SemCtr被清零为0,但这不一定是应用想要的初始值!

// 你可能希望:
// - 二值信号量:初始值=1(可用)
// - 事件计数:初始值=0(无事件)  
// - 资源管理:初始值=N(N个资源)
// 操作系统不知道你的用途,所以需要你明确设置
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 内核中任务信号量和任务队列的设计哲学
    • 核心答案:因为设计理念不同
      • 任务信号量:需要明确初始状态
      • 任务消息队列:初始状态是确定的
    • 实际场景对比
      • 任务信号量的不同用途:
      • 任务消息队列的唯一用途:
    • 内核代码深度剖析
      • 1. 在 OSTaskCreate() 函数内部
      • 2. OS_TaskInitTCB() 函数的详细实现
      • 3. 消息队列的专门初始化函数
    • 完整的初始化流程
      • 步骤1:内存分配
      • 步骤2:调用 OSTaskCreate()
      • 步骤3:操作系统执行初始化
      • 步骤4:初始化完成后的TCB状态
    • 关键问题:为什么信号量还需要单独设置?
      • 消息队列的初始化:
      • 信号量计数器的初始化:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档