理解RTOS多任务系统,仿写 目录一、理解 FreeRTOS 任务调度的底层原理二、仿写 FreeRTOS 任务调度器1. 任务创建函数设计2. 调度器触发机制3. 中断处理流程4. 任务启动机制5. 任务切换过程6. 上下文恢复7. 实现效果展示一、理解 FreeRTOS 任务调度的底层原理二、仿写 FreeRTOS 任务调度器1. 任务创建函数设计首先需要实现一个任务创建函数其参数包括函数指针任务入口、函数参数、栈空间地址、栈大小。// __attribute__((aligned(4))) 实现4字节对齐 static char stack_a[1024] __attribute__((aligned(4))); static char stack_b[1024] __attribute__((aligned(4))); static char stack_c[1024] __attribute__((aligned(4))); // 任务函数1循环输出字符 void task(void *parameter) { char c (char)parameter; while(1) { putchar(c); } } // 任务函数2计算累加和并显示 void task2(void *parameter) { int i; int sum 0; for(i 0; i 100; i) { sum i; } while(1) { put_s_hex(sum , sum); } } // 主函数创建并启动任务 int mymain() { create_task(task, a, stack_a, 1024); create_task(task, b, stack_b, 1024); create_task(task2, 0, stack_c, 1024); start_task(); return 0; }这个创建函数的核心作用是伪造任务上下文包括硬件自动保存的寄存器R0-R3, R12, LR, PC, PSR和需要软件保存的寄存器R4-R11。void create_task(task_function f, void *parameter, char *stack, int stack_len) { int *top (int *)(stack stack_len); /* 伪造任务上下文模拟中断现场 */ top - 16; // 为16个寄存器预留空间 /* R4-R11软件保存的寄存器 */ top[0] 0; // r4 top[1] 0; // r5 top[2] 0; // r6 top[3] 0; // r7 top[4] 0; // r8 top[5] 0; // r9 top[6] 0; // r10 top[7] 0; // r11 /* R0-R3硬件自动保存的寄存器 */ top[8] (int)parameter; // r0任务参数 top[9] 0; // r1 top[10] 0; // r2 top[11] 0; // r3 top[12] 0; // r12 /* LR链接寄存器特殊处理 */ top[13] 0; // lr不关心因为任务退出不会返回到main /* 返回地址任务入口函数 */ top[14] (int)f; // PC任务入口地址 /* PSR程序状态寄存器 */ top[15] (1 24); // 使用Thumb指令集 /* 记录栈顶位置到任务栈数组 */ task_stacks[task_count] (int)top; }2. 调度器触发机制调度器由系统时钟中断触发。每次SysTick中断发生时都会检查是否需要执行任务切换。当发生SysTick_Handler中断时硬件会自动保存以下寄存器到当前任务的栈中我们需要在中断处理函数中软件保存剩余的寄存器R4-R11以及LR寄存器的特殊值。同时需要记录当前栈的位置并传递给SysTick_Handler函数。3. 中断处理流程进入SysTick_Handler函数后首先进行状态判断如果任务尚未创建完成直接返回如果只有一个任务直接启动即可无需切换如果有多个任务执行任务切换逻辑4. 任务启动机制任务启动的关键是从栈中恢复寄存器的值。在上下文切换时会传递两个重要参数R0栈地址新任务的栈顶R1LR的特殊值用于异常返回5. 任务切换过程当需要切换任务时需要执行以下操作保存当前任务的上下文到其栈中恢复新任务的上下文从其栈中更新当前任务指针切换栈指针SP到新任务的栈在SysTick_Handler函数中主要工作是更新新旧任务的SP地址完成任务切换。6. 上下文恢复切换到新任务后需要找到新任务的栈并恢复所有寄存器的值然后跳转到任务入口点继续执行。这个过程通常发生在中断服务程序如 SysTick_Handler的末尾通过执行特定的汇编指令序列来完成。上下文恢复的核心步骤以 ARM Cortex-M 为例获取新任务的栈指针调度器决定切换到哪个任务后将该任务的栈顶地址即保存的上下文起始地址加载到某个寄存器例如 R0。恢复软件保存的寄存器R4-R11执行指令LDMIA SP!, {r4 - r11}。这条指令从当前栈指针SP指向的内存地址开始依次将值弹出到寄存器 R4, R5, ..., R11 中同时栈指针 SP 会递增因为后缀!表示写回。这一步恢复的是在中断入口时由软件手动压栈的寄存器。恢复硬件自动保存的寄存器R0-R3, R12, LR, PC, PSR执行指令LDMIA SP!, {r0}。注意这里的{r0}是一个简写实际在异常返回流程中硬件会从栈中自动恢复一整套寄存器。在典型的 PendSV 或中断退出序列中会使用类似LDMIA SP!, {r0, r1, r2, r3, r12, lr, pc, psr}的指令或由硬件自动完成。文章示例中简化为LDMIA SP!, {r0}可能是一个示意代表恢复关键参数寄存器。更完整的恢复会触发异常返回机制将 PC 恢复为任务入口点PSR 恢复为任务状态。执行中断返回通过一条特殊的返回指令如BX LR或直接触发异常返回跳转到新任务的代码位置继续执行。两条指令的执行时机LDMIA SP!, {r4 - r11}在中断服务程序中在决定切换任务并加载了新任务的栈指针之后、执行中断返回之前运行。它负责恢复被调用者保存的寄存器。LDMIA SP!, {r0}或完整的恢复指令紧随其后执行在恢复 R4-R11 之后、中断返回指令之前。它负责恢复剩余的上下文并最终将 PC 指向新任务要执行的指令地址。这两条指令共同完成了从新任务栈中“弹出”整个处理器现场的过程是任务切换中“恢复”环节的具体实现。7. 实现效果展示最终实现的仿FreeRTOS调度器能够正确创建多个任务并在SysTick中断的驱动下进行任务切换实现多任务并发执行的效果。