MPU在Cortex-M中支持任务栈隔离,但仅限M3/M4/M7/M33/M55等带MPU的型号;M0/M0+无MPU。需手动使能并配置区域,每个任务栈应设为独立、非重叠、含guard page的不可执行区域,且基地址与大小须严格对齐。

MPU在Cortex-M中是否支持任务栈隔离?
支持,但仅限Cortex-M3/M4/M7/M33/M55(带MPU的型号),M0/M0+无MPU。关键前提是:你用的是裸机环境(无RTOS)或RTOS已关闭MPU自动管理(如FreeRTOS默认不启用MPU,除非用FreeRTOSConfig.h中configUSE_MPU_WRAPPERS设为1)。很多开发者卡在这一步——以为芯片有MPU就自动生效,其实它默认是disabled状态,必须手动配置并使能。
怎么用ARM CMSIS库配置MPU保护单个任务栈?
不用手写汇编,直接调CMSIS的MPU_Region_InitTypeDef结构体 + MPU_ConfigRegion()函数。核心逻辑是:给每个任务栈分配独立、非重叠的内存区域,设为“不可执行+用户态只读/只写+特权态可读写”,并禁用cache(避免MPU与cache行为冲突)。
常见错误现象:HardFault_Handler触发,且SCB->CFSR显示MPU_FAULT(0x02000000);或者任务一运行就死,但栈指针psp看起来正常——大概率是region size填错(必须是2的幂,最小32字节,填MPU_REGION_SIZE_32B而非32)或base地址没对齐(比如size=1KB,base必须是1KB对齐)。
- 栈起始地址必须按region size对齐(如设size=256B,则
stack_base & 0xFF必须为0) - 务必调用
MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk),否则即使配置了region也不生效 - 切换任务时,若用PSP(Process Stack Pointer),需在
PendSV里手动__set_PSP(new_stack_top),MPU region本身不随栈指针自动切换——它是静态映射,不是按任务动态加载的
为什么FreeRTOS + MPU常导致栈溢出检测失效?
因为FreeRTOS的MPU wrapper会把整个任务栈区域设为“用户可读写”,但不会实时检查栈顶是否越过边界;它只靠MPU硬件捕获越界访问——而栈溢出往往先写到相邻region的间隙(padding区)或未映射空洞,此时MPU不报错。更糟的是,若两个任务栈region配置成相邻且无gap,一个溢出直接踩进另一个栈,MPU也管不了。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 每个任务栈region后强制留一段
MPU_REGION_SIZE_32B的“不可访问guard page”(属性设为MPU_REGION_NO_ACCESS) - 栈大小申请时多加32字节,并确保初始化时用
memset(stack, 0xCC, size),便于调试时观察溢出痕迹 - 禁用
configCHECK_FOR_STACK_OVERFLOW的level 1(只检查栈顶magic word),改用level 2(扫描整个栈区找0xCC断点),因为它不依赖MPU
MPU region数量不够怎么办?
Cortex-M4典型只有8个region(M7最多16个),而你可能有10个任务+堆+外设寄存器+代码段——硬凑不够。别硬塞,优先保关键项:每个任务栈1个region、主栈(MSP)1个、堆区1个、外设寄存器区1个。其余如代码段、常量区,用MPU设为“只读+执行”即可,它们不常变,复用region更安全。
容易被忽略的点:MPU region一旦启用,所有访问都受约束,包括中断服务程序(ISR)里的栈操作。如果ISR用的是MSP,而你把MSP region设得太小(比如只给128B),一个带浮点寄存器压栈的中断就直接触发MPU fault。所以MSP region size至少留512B余量,并确认MPU->RASR中SRD(sub-region disable)字段没误关掉某段。


















