首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >FreeRTOS多任务切换底层原理详解

FreeRTOS多任务切换底层原理详解

作者头像
云深无际
发布2026-01-07 11:52:28
发布2026-01-07 11:52:28
1640
举报
文章被收录于专栏:云深之无迹云深之无迹

顿悟了,一下子就顿悟了,其实就是看到 keli 调试的时候有很多的汇编代码,运行的那么快,然后就突然对 FreeRTOS 很有感觉了;就是运行的太快,即使不停的切换我们也没有感觉。


1. 多任务执行的本质

1.1 核心概念

在单核MCU上,多任务并不是真正的并行执行,而是通过快速的任务切换实现的"时间分片"。

1.2 基本原理

对MCU硬件特性来说单核MCU依然是一条指令一条指令按顺序执行;而RTOS通过系统定时器定期中断,保存/恢复任务状态;对用户感知来说由于切换频率极高(通常1ms),用户感觉多个任务在"同时"运行

2. 时间分片机制

2.1 时间分片示意图

代码语言:javascript
复制
时间轴:   |----1ms----|----1ms----|----1ms----|----1ms----|----1ms----|
任务执行: [ 任务A执行 ] [ 任务B执行 ] [ 任务C执行 ] [ 任务A执行 ] [ 任务B执行 ]
中断:     ↑SysTick    ↑SysTick    ↑SysTick    ↑SysTick    ↑SysTick
状态:     [保存A]     [保存B]     [保存C]     [保存A]     [保存B]
          [加载B]     [加载C]     [加载A]     [加载B]     [加载C]

2.2 系统定时器(SysTick)配置

代码语言:javascript
复制
// 典型的SysTick配置
#define configTICK_RATE_HZ           1000        // 1000Hz = 1ms时间片
#define configCPU_CLOCK_HZ           100000000   // 100MHz CPU

// SysTick初始化
SysTick_Config(configCPU_CLOCK_HZ / configTICK_RATE_HZ);  // 每1ms触发一次

2.3 时间片特点

特性

说明

时间片长度

通常1ms,可配置

中断频率

1000Hz(1ms间隔)

切换开销

几十个CPU周期

用户感知

无感知,任务似乎并行运行


3. 任务切换的详细过程

3.1 任务切换四个步骤

步骤1: 系统定时器中断触发
代码语言:javascript
复制
/**
 * @brief SysTick中断处理函数
 * @note 每1ms触发一次,是任务调度的起点
 */
void xPortSysTickHandler(void)
{
    uint32_t ulPreviousMask;
    
    ulPreviousMask = portSET_INTERRUPT_MASK_FROM_ISR();
    {
        /* 系统节拍计数器递增 */
        if( xTaskIncrementTick() != pdFALSE )
        {
            /* 需要进行任务切换,触发PendSV中断 */
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
        }
    }
    portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask );
}
步骤2: PendSV中断处理上下文切换
代码语言:javascript
复制
/**
 * @brief PendSV中断处理程序(ARM Cortex-M系列汇编代码)
 */
xPortPendSVHandler:
    /* 1. 保存当前任务的CPU寄存器到任务堆栈 */
    mrs     r0, psp                     ; 获取当前任务堆栈指针(PSP)
    ldr     r3, =pxCurrentTCB           ; 获取当前TCB地址
    ldr     r2, [r3]                    ; 加载当前TCB指针
    stmdb   r0!, {r4-r11}               ; 保存寄存器R4-R11到堆栈
    str     r0, [r2]                    ; 保存新的堆栈指针到TCB

    /* 2. 调用C语言任务调度器 */
    push    {r3, r14}                   ; 保护寄存器
    cpsid   i                           ; 禁用中断
    bl      vTaskSwitchContext          ; 调用任务调度器
    cpsie   i                           ; 启用中断
    pop     {r2, r3}                    ; r2=新TCB地址,r3=返回地址

    /* 3. 恢复新任务的上下文 */
    ldr     r1, [r2]                    ; 获取新任务TCB
    ldr     r0, [r1]                    ; 获取新任务的堆栈指针
    ldmia   r0!, {r4-r11}               ; 从堆栈恢复寄存器R4-R11
    msr     psp, r0                     ; 设置新的堆栈指针

    bx      r3                          ; 返回,继续执行新任务
步骤3: 任务调度器选择下一个任务
代码语言:javascript
复制
/**
 * @brief 任务切换上下文函数
 */
void vTaskSwitchContext( void )
{
    if( uxSchedulerSuspended != ( UBaseType_t ) 0U )
    {
        /* 调度器被暂停,不允许任务切换 */
        xYieldPendings[ 0 ] = pdTRUE;
    }
    else
    {
        /* 记录任务运行时间统计 */
        #if ( configGENERATE_RUN_TIME_STATS == 1 )
        {
            ulTotalRunTime[ 0 ] = portGET_RUN_TIME_COUNTER_VALUE();
            if( ulTotalRunTime[ 0 ] > ulTaskSwitchedInTime[ 0 ] )
            {
                pxCurrentTCB->ulRunTimeCounter += 
                    ( ulTotalRunTime[ 0 ] - ulTaskSwitchedInTime[ 0 ] );
            }
            ulTaskSwitchedInTime[ 0 ] = ulTotalRunTime[ 0 ];
        }
        #endif

        /* 检查栈溢出 */
        taskCHECK_FOR_STACK_OVERFLOW();

        /* 选择优先级最高的就绪任务 */
        taskSELECT_HIGHEST_PRIORITY_TASK();

        /* 平台特定的任务切换后处理 */
        portTASK_SWITCH_HOOK( pxCurrentTCB );
    }
}

4. 任务调度算法

4.1 通用算法 vs 硬件优化算法

算法类型

时间复杂度

适用平台

优缺点

通用算法

O(n)

所有平台

兼容性好,但速度较慢

硬件优化

O(1)

支持CLZ指令的ARM

速度极快,硬件加速

4.2 通用任务选择算法

代码语言:javascript
复制
/**
 * @brief 通用任务选择算法(线性搜索)
 */
#define taskSELECT_HIGHEST_PRIORITY_TASK()                               \
do {                                                                     \
    UBaseType_t uxTopPriority = uxTopReadyPriority;                     \
                                                                         \
    /* 找到最高优先级且有就绪任务的队列 */                                 \
    while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) != pdFALSE ) \
    {                                                                    \
        configASSERT( uxTopPriority );                                   \
        --uxTopPriority;                                                 \
    }                                                                    \
                                                                         \
    /* 从该优先级的任务列表中选择下一个任务(时间片轮转) */                 \
    listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
    uxTopReadyPriority = uxTopPriority;                                  \
} while( 0 )

4.3 硬件优化版本(O(1)时间复杂度)

代码语言:javascript
复制
/**
 * @brief 硬件优化的任务选择算法
 */
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 )

/* 位图操作宏 */
#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) \
    ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )

#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) \
    ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )

/* 使用CLZ指令快速找到最高优先级 */
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) \
    uxTopPriority = ( 31UL - ( ( uint32_t ) __CLZ( ( uxReadyPriorities ) ) ) )

#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */

4.4 CLZ指令工作原理

代码语言:javascript
复制
/**
 * @brief CLZ指令示例
 * CLZ = Count Leading Zeros(计算前导零个数)
 */

// 示例:假设uxReadyPriorities = 0x00000110 
//       (二进制: 00000000 00000000 00000001 00010000)
//       CLZ(0x00000110) = 28 (前面有28个0)
//       最高优先级 = 31 - 28 = 3

uint32_t uxReadyPriorities = 0x00000110;  // 优先级0和优先级8的任务就绪
uint32_t uxTopPriority = 31 - __CLZ(uxReadyPriorities);  // 结果:uxTopPriority = 8

5. 多任务状态管理

5.1 任务控制块(TCB)核心结构

代码语言:javascript
复制
/**
 * @brief 任务控制块结构定义(简化版)
 */
typedefstruct tskTaskControlBlock
{
    volatile StackType_t    *pxTopOfStack;      /* 栈顶指针 - 必须是第一个成员 */
    ListItem_t              xStateListItem;     /* 状态列表项 */
    ListItem_t              xEventListItem;     /* 事件列表项 */
    UBaseType_t             uxPriority;         /* 任务优先级 */
    StackType_t             *pxStack;           /* 栈底指针 */
    char                    pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名称 */
    
    #if ( configGENERATE_RUN_TIME_STATS == 1 )
        configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /* 运行时间计数器 */
    #endif
    
    #if ( configUSE_TASK_NOTIFICATIONS == 1 )
        volatileuint32_t   ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
        volatileuint8_t    ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    #endif
    
} tskTCB;

typedef tskTCB TCB_t;

5.2 任务状态定义

代码语言:javascript
复制
/**
 * @brief 任务状态枚举
 */
typedef enum
{
    eRunning = 0,    /* 运行状态 - 任务正在CPU上执行 */
    eReady,          /* 就绪状态 - 任务可以运行,等待调度 */
    eBlocked,        /* 阻塞状态 - 任务等待事件或延时 */
    eSuspended,      /* 挂起状态 - 任务被明确挂起 */
    eDeleted,        /* 删除状态 - 任务已删除 */
    eInvalid         /* 无效状态 */
} eTaskState;

5.3 任务列表管理

代码语言:javascript
复制
/**
 * @brief 任务列表定义
 */

/* 就绪任务列表 - 按优先级分组 */
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];

/* 延时任务列表 */
PRIVILEGED_DATA static List_t xDelayedTaskList1;
PRIVILEGED_DATA static List_t xDelayedTaskList2;
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;

/* 挂起任务列表 */
#if ( INCLUDE_vTaskSuspend == 1 )
    PRIVILEGED_DATA static List_t xSuspendedTaskList;
#endif

/* 当前TCB指针 */
#if ( configNUMBER_OF_CORES == 1 )
    PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL;
#else
    PRIVILEGED_DATA TCB_t * volatile pxCurrentTCBs[ configNUMBER_OF_CORES ] = { NULL };
#endif
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-09-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云深之无迹 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 多任务执行的本质
    • 1.1 核心概念
    • 1.2 基本原理
  • 2. 时间分片机制
    • 2.1 时间分片示意图
    • 2.2 系统定时器(SysTick)配置
    • 2.3 时间片特点
  • 3. 任务切换的详细过程
    • 3.1 任务切换四个步骤
      • 步骤1: 系统定时器中断触发
      • 步骤2: PendSV中断处理上下文切换
      • 步骤3: 任务调度器选择下一个任务
  • 4. 任务调度算法
    • 4.1 通用算法 vs 硬件优化算法
    • 4.2 通用任务选择算法
    • 4.3 硬件优化版本(O(1)时间复杂度)
    • 4.4 CLZ指令工作原理
  • 5. 多任务状态管理
    • 5.1 任务控制块(TCB)核心结构
    • 5.2 任务状态定义
    • 5.3 任务列表管理
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档