FreeRTOS源码解析(2)任务挂起与恢复

1.任务挂起

  • 功能 :将指定任务挂起。被挂起的任务不再参与调度,直到被其他任务调用 vTaskResume() 恢复。

  • 参数xTaskToSuspend 是要挂起的任务句柄,若为 NULL 则挂起调用者自身。

  • 注意 :挂起不同于删除,任务控制块和栈内存不会被释放 ,只是任务状态变为 Suspended,挂起期间保留所有资源,可以被唤醒

    void vTaskSuspend( TaskHandle_t xTaskToSuspend )
    {
    TCB_t * pxTCB;

    复制代码
          taskENTER_CRITICAL();       //程序进入临界段
          {
              /* If null is passed in here then it is the running task that is
               * being suspended. */
              /* 若传入 NULL,返回当前任务(即自挂起),反之返回该任务 */
              pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
    
              traceTASK_SUSPEND( pxTCB );     //跟踪宏,用户可自定义用于参数检查,默认未实现
    
              /* Remove task from the ready/delayed list and place in the
               * suspended list. */
              /* 将该任务从就绪/延迟任务列表中删除 */
              /* 注意:挂起任务会自动移除任何当前的阻塞延时(即延时状态被取消,但任务不会重新就绪,而是直接挂起) */
              if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
              {
                  /* 返回0表示移除元素后该优先级链表变空,因此需要复位优先级位 */
                  taskRESET_READY_PRIORITY( pxTCB->uxPriority );  //清除就绪位图中该优先级位,以便调度器知道该优先级不再有就绪任务
              }
              else
              {
                  mtCOVERAGE_TEST_MARKER();       //用于代码覆盖率测试的宏,此处等价于一个空语句
              }
    
              /* 处理事件等待列表  如果任务正阻塞在事件上(容器非空),则将其从该事件列表中移除;否则不做处理 */
              /* 如果任务正阻塞在某个事件(如队列、信号量、事件组)上,将它从该事件的等待列表中移除。
               * 一旦挂起,任务不再等待任何事件,必须脱离等待队列,否则该事件对象可能保留无效的等待项,导致后续出错 */
              /* Is the task waiting on an event also? */
              if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
              {
                  ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
              }
              else
              {
                  mtCOVERAGE_TEST_MARKER();       //用于代码覆盖率测试的宏,此处等价于一个空语句
              }
    
              /* 将任务的状态列表项插入全局挂起列表 xSuspendedTaskList 的末尾
               * 插入末尾而非按优先级排序,因为挂起任务不参与调度,排序无意义,仅用作收纳 */
              vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
    
              /* 如果启用了任务通知功能,每个任务有 configTASK_NOTIFICATION_ARRAY_ENTRIES 个通知状态 */
              #if ( configUSE_TASK_NOTIFICATIONS == 1 )
              {
                  BaseType_t x;
    
                  for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
                  {
                      /* 如果某通知状态为 taskWAITING_NOTIFICATION(即任务正等待一个通知而阻塞),由于任务将挂起,还没有收到通知,
                       * 此处直接将状态改为 taskNOT_WAITING_NOTIFICATION,表示不再等待 */
                      if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
                      {
                          /* The task was blocked to wait for a notification, but is
                           * now suspended, so no notification was received. */
                          pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
                      }
                  }
              }
              #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
          }
          taskEXIT_CRITICAL();        //程序退出临界段
    
          /* 如果调度器处于运行状态,则重新计算下一次任务解除阻塞的时间【临界段中运行,防止状态中途改变】 */
          if( xSchedulerRunning != pdFALSE )
          {
              /* Reset the next expected unblock time in case it referred to the
               * task that is now in the Suspended state. */
              taskENTER_CRITICAL();       //程序进入临界段
              {
                  prvResetNextTaskUnblockTime();      //刷新下一个任务的阻塞时间
              }
              taskEXIT_CRITICAL();        //程序退出临界段
          }
          else
          {
              mtCOVERAGE_TEST_MARKER();       //用于代码覆盖率测试的宏,此处等价于一个空语句
          }
    
          /* 如果挂起的是当前任务,进行任务切换处理 */
          if( pxTCB == pxCurrentTCB )
          {
              /* 任务调度器处于运行状态,触发 PendSV 异常,执行上下文切换到其他就绪任务 */
              if( xSchedulerRunning != pdFALSE )
              {
                  /* The current task has just been suspended. */
                  configASSERT( uxSchedulerSuspended == 0 );
                  portYIELD_WITHIN_API();
              }
              else        //调度器未运行
              {
                  /* The scheduler is not running, but the task that was pointed
                   * to by pxCurrentTCB has just been suspended and pxCurrentTCB
                   * must be adjusted to point to a different task. */
                  /* 如果挂起任务列表的长度等于系统中任务总数,说明所有任务都已被挂起,没有就绪任务了。
                   * 这时设置 pxCurrentTCB = NULL,表示当前没有可运行的任务。等到将来有新任务创建或某个任务被恢复时,调度器会再次指定当前任务 */
                  if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */
                  {
                      /* No other tasks are ready, so set pxCurrentTCB back to
                       * NULL so when the next task is created pxCurrentTCB will
                       * be set to point to it no matter what its relative priority
                       * is. */
                      pxCurrentTCB = NULL;
                  }
                  /* 否则:系统中还有其他任务不在挂起列表中(即仍处于就绪或阻塞态),调用 vTaskSwitchContext()
                   * 选择一个新的最高优先级任务作为 pxCurrentTCB。这发生在调度器未启动时,所以直接进行软件切换而不是 pendSV。 */
                  else    
                  {
                      vTaskSwitchContext();
                  }
              }
          }
          else
          {
              mtCOVERAGE_TEST_MARKER();       //用于代码覆盖率测试的宏,此处等价于一个空语句
          }
      }

2.任务恢复

  • 功能:将一个先前被挂起的任务恢复为就绪状态,使其重新参与调度。

  • 参数xTaskToResume 为要恢复的任务句柄。

  • 关键约束不能NULL,也不能是当前正在执行的任务(因为当前任务不可能是挂起状态)

    void vTaskResume( TaskHandle_t xTaskToResume )
    {
    TCB_t * const pxTCB = xTaskToResume;

    复制代码
          /* It does not make sense to resume the calling task. */
          /* 断言确保参数不为 NULL */
          configASSERT( xTaskToResume );
    
          /* The parameter cannot be NULL as it is impossible to resume the
           * currently executing task. */
          /* 只有在恢复非当前任务且非空时才执行实际操作 */
          if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )
          {
              taskENTER_CRITICAL();       //程序进入临界段
              {
                  /* 只有确实被挂起的任务才需要恢复。如果任务处于就绪、阻塞、运行或删除状态,这个检查会失败,直接跳过 */
                  if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
                  {
                      traceTASK_RESUME( pxTCB );      //调试宏,需要用户自己实现
    
                      /* The ready list can be accessed even if the scheduler is
                       * suspended because this is inside a critical section. */
                      /* 将任务的状态列表项从 xSuspendedTaskList 中摘除 */
                      ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
                      /* /* 将任务加入就绪队列:尾插到对应优先级链表,并置位就绪位图 */ */
                      prvAddTaskToReadyList( pxTCB );
    
                      /* A higher priority task may have just been resumed. */
                      /* 如果刚恢复的任务优先级大于或等于当前任务的优先级,就需要进行一次调度判断 */
                      if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
                      {
                          /* This yield may not cause the task just resumed to run,
                           * but will leave the lists in the correct state for the
                           * next yield. */
                          /* 在抢占式调度(configUSE_PREEMPTION == 1)时,等于 portYIELD(),立即触发 PendSV 进行上下文切换。
                           * 在合作式调度时,是空宏,不强制切换,等待当前任务主动让出 CPU。 */
                          taskYIELD_IF_USING_PREEMPTION();
                      }
                      else
                      {
                          mtCOVERAGE_TEST_MARKER();   //代码覆盖率测试,实际为空语句
                      }
                  }
                  else
                  {
                      mtCOVERAGE_TEST_MARKER();       //代码覆盖率测试,实际为空语句
                  }
              }
              taskEXIT_CRITICAL();        //程序退出临界段
          }
          else
          {
              mtCOVERAGE_TEST_MARKER();       //代码覆盖率测试,实际为空语句
          }
      }

3.就绪位图

3.1 定义

就绪位图是 FreeRTOS 多优先级调度的"索引":

  • 一个整数(通常 UBaseType_t,32 位),每一位代表一个优先级是否有任务就绪。
  • bit0 → 优先级 0,bit1 → 优先级 1,...,bit31 → 优先级 31。
  • 如果某优先级有就绪任务,对应位置 1;该优先级就绪列表为空,则对应位清 0

3.2 核心作用

调度器利用硬件前导零计数指令(__clz)在 O(1) 时间内找到最高就绪优先级,从而快速选出下一个运行任务,延迟恒定

3.3 如果优先级数量超过32个怎么办?

你的定义基于 32 位整数 ,隐含了 configMAX_PRIORITIES ≤ 32

如果配置的优先级数超过 32,FreeRTOS 会自动使用多个 32 位字组成就绪位图数组,例如:

复制代码
#define portPRIORITY_GROUPS   ( ( configMAX_PRIORITIES + 31 ) / 32 )
static UBaseType_t uxReadyPriorities[ portPRIORITY_GROUPS ];

4.声明

(1)Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.

(2)文中代码来自FreeRTOS,遵循MIT许可证,许可证可参考:https://opensource.org/licenses/MIT

复制代码
/*
 * FreeRTOS Kernel V10.5.1
 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */
相关推荐
万粉变现经纪人2 小时前
如何解决 pip install bitsandbytes 报错 仅支持 Linux+glibc(macOS/Windows 失败)问题
linux·运维·windows·python·scrapy·macos·pip
Hello.Reader3 小时前
Windows C 盘空间告急?用 PowerShell 写一个安全可控的清理脚本
c语言·windows·安全
阿洛学长3 小时前
OpenClaw零成本部署指南:Windows/Mac/Linux/阿里云搭建+两个免费大模型API配置攻略
linux·windows·macos
嵌入式Q3 小时前
FreeRTOS源码解析(2)任务调度器挂起与恢复
windows
飘飘叶4 小时前
[FRP]Windows 安装 frpc 客户端,以及P2P方式ssh配置
windows·frp
承渊政道4 小时前
用群晖部署OmniBox+pansou:把分散的影视资源全聚合到一个界面里
服务器·windows·网络协议·https·ip·视频·持续部署
阿昭L4 小时前
Windows中的I/O完成通知与事件内核对象
windows·多线程
l1t13 小时前
DeepSeek辅助解决windows 11 wsl2中Linux版Dbeaver显示中文
linux·运维·windows
love530love15 小时前
Clink 调校指南:让 Windows CMD 拥有现代终端的便捷体验
人工智能·windows·python·cmd·clink