FreeRTOS内部机制(一)
答:FreeRTOS 并不通过复杂的“状态机”变量来记录每个任务在哪里,而是通过。每个优先级,都有一个就绪链表:pxReadyTasksLists[优先级]有很多任务都想运行,优先级各不相同,怎么管理它们?文件中,主要职责是决定**“下一个运行谁”**。答:初始化任务时,把函数指针存入了栈里(PC)提问:在TCB_t里怎么没看到函数指针?,来直观地表示和执行任务状态的改变。这是调度的“大脑”。问:
·
文章目录
FreeRTOS内部机制(一)
1.任务创建

1.1参数解析
- 任务创建出来需要做什么?需要提供函数
- 任务随时会被切换,寄存器保存在哪?需要提供栈,可以静态分配(数组),也可以动态分配
- 怎么记录这些信息:栈在哪里?需要有一个任务结构体
1.1.1任务结构体

TCB_t用来表示一个任务,它的重要成员如下:
- pxTopOfStack:执行栈里的最后一个元素
- xStateListItem:通过它把当前任务放入某个状态链表(Ready, Delayed, Suspended)
- xEventListItem:比如任务在等待队列A,则通过xEventListItem把自己放入队列A的链表
- uxPriority:任务的原始优先级
- pxStack:栈的起始位置
- pxEndOfStack:栈顶,栈的最高的、有效地址
- uxBasePriority:任务的当前优先级
提问:在TCB_t里怎么没看到函数指针?
答:初始化任务时,把函数指针存入了栈里(PC)
1.2任务创建的过程
xTaskCreate
// 1. 分配任务结构体
TCB_t * pxNewTCB;
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
// 2. 分配栈
pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ... );
// 3. 初始化任务栈,即构造TCB_t的内容
prvInitialiseNewTask
// 3.1 初始化优先级、队列等等
pxNewTCB->uxPriority = uxPriority;
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
// 3.2 初始化栈
pxNewTCB->pxTopOfStack = pxPortInitialiseStack(...)
// 4. 放入就绪链表
prvAddNewTaskToReadyList( pxNewTCB );
2.任务的调度机制
2.1使用链表来管理任务
有很多任务都想运行,优先级各不相同,怎么管理它们?

每个优先级,都有一个就绪链表:pxReadyTasksLists[优先级]
任务被创建时,要使用prvAddNewTaskToReadyList()来把它放入对应的就绪链表,调用过程为:
xTaskCreate
->prvAddNewTaskToReadyList
->vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) );
2.2 使用链表来理解调度机制
2.2.1 第1个运行任务是谁?
最高优先级的ready list里最后一个创建的任务:

2.2.2 抢占
最高优先级的ready list里的第1个任务永远可以即刻执行:

2.2.3 使用链表和Tick来理解时间片轮转
问:谁来执行任务调度?
答:由 内核调度器(Kernel Scheduler) 通过 硬件中断 和 软件协作 共同实现的
1.时间驱动者:SysTick 中断
- 机制:
- Cortex-M 内核自带的 SysTick 定时器 每隔一个固定的时间(通常是 1ms,即
configTICK_RATE_HZ)产生一次中断 - 中断服务函数
xPortSysTickHandler()被调用
- Cortex-M 内核自带的 SysTick 定时器 每隔一个固定的时间(通常是 1ms,即

- 工作内容:
- 更新系统滴答计数 (
xTickCount++) - 检查是否有延时的任务到期了(将它们从延时列表移回就绪列表)
- 如果开启了时间片轮转 (
configUSE_TIME_SLICING),且当前有多个同优先级的任务,它会标记需要进行上下文切换 - 如果需要切换,它会触发 PendSV 中断
- 更新系统滴答计数 (
2.事件驱动者:PendSV 中断
- 为什么需要它?
- 为了保证实时性,FreeRTOS 不希望在中断(如 SysTick 或外设中断)还没处理完时就急着切换任务
- 因此,当调度器决定“该换人了”,它不会立即切换,而是挂起一个 PendSV (Pendable Request for System Service) 异常
- 工作机制:
- PendSV 的优先级被设置为全系统最低
- 只有当所有其他高优先级的硬件中断(如串口、定时器、SysTick)都处理完毕后,CPU 才会去处理 PendSV
- PendSV_Handler 被执行:
- 保存当前任务的上下文(R4-R11 等)到当前任务的栈中
- 调用
vTaskSwitchContext()选择新任务 - 从新任务的栈中恢复上下文
- 返回,CPU 开始执行新任务的代码
3.核心执行者:调度器内核代码
这是调度的“大脑”。它包含在 tasks.c 文件中,主要职责是决定**“下一个运行谁”**。
- 关键函数:
vTaskSwitchContext()- 这个函数会调用我们之前讨论过的宏
taskSELECT_HIGHEST_PRIORITY_TASK()。 - 它扫描就绪列表,找到优先级最高的任务,并更新全局指针
pxCurrentTCB指向该任务。
- 这个函数会调用我们之前讨论过的宏
- 触发时机:当系统检测到需要切换任务时(如时间片到期、高优先级任务就绪),就会调用这个函数
2.3任务状态的切换
2.3.1状态转换图

2.3.2核心:链表
怎么理解?
答:FreeRTOS 并不通过复杂的“状态机”变量来记录每个任务在哪里,而是通过将任务控制块(TCB)在不同的链表之间“剪贴”和“移动”,来直观地表示和执行任务状态的改变

typedef struct tskTaskControlBlock {
volatile StackType_t *pxTopOfStack; /* 栈顶指针 */
/* ... 其他信息 ... */
ListItem_t xStateListItem; /* 【关键】用于挂在“状态链表”中的节点 */
ListItem_t xEventListItem; /* 【关键】用于挂在“事件链表”中的节点 */
UBaseType_t uxPriority; /* 优先级 */
/* ... */
} TCB_t;
- 重点:每个 TCB 内部嵌入了
ListItem_t(列表项) - 作用:这就好比每个人(TCB)手里都拿着一个钩子(ListItem),可以随时把自己挂到不同的队伍(链表)里
更多推荐



所有评论(0)