ARM内联汇编保存栈指针问题:RTOS上下文切换编译报错排查
RTOS上下文切换内联汇编问题解决
核心问题分析
你原来的代码有两个关键错误:
- 扩展内联汇编的约束用反了:第一个
asm块用了输出约束"=r"(OsCurrent),这会让编译器生成把r0的值写入OsCurrent的逻辑,但你实际需要把OsCurrent的地址加载到r0,应该用输入约束。 - 拆分多个独立
asm块不安全:编译器可能在块之间插入优化代码,破坏上下文切换的原子性。
正确实现代码
把保存当前栈指针的逻辑合并到一个asm块,用输入约束传递OsCurrent的地址,同时加上volatile防止编译器优化掉关键操作:
// 保存当前线程的栈指针:OsCurrent->sp = sp; asm volatile( "LDR r1, [r0] \n\t" // r1 = OsCurrent(指向线程对象的指针) "MOV r4, sp \n\t" // r4 = 当前栈指针sp "STR r4, [r1] \n\t" // OsCurrent->sp = r4(保存sp) : // 输出约束:无 : "r"(OsCurrent) // 输入约束:把OsCurrent的地址放到r0 : "r0", "r1", "r4" // 告诉编译器这些寄存器被修改了,避免冲突 );
如果要完成完整的上下文切换(保存R4-R11、切换栈、恢复新线程的R4-R11),完整代码可以这么写:
extern "C" void ContextSwitch(void) { // 1. 保存当前线程的上下文(R4-R11入栈) asm volatile( "PUSH {r4-r11} \n\t" // 保存非volatile寄存器到当前栈 "LDR r1, [r0] \n\t" // r1 = OsCurrent "STR sp, [r1] \n\t" // OsCurrent->sp = sp(保存当前栈指针) : : "r"(OsCurrent) : "r0", "r1", "sp", "memory" ); // 2. 切换到新线程(这里假设OsNext是下一个线程的指针) // 从OsNext获取新栈指针 asm volatile( "LDR r0, [r1] \n\t" // r0 = OsNext "LDR sp, [r0] \n\t" // sp = OsNext->sp(切换栈指针) "POP {r4-r11} \n\t" // 恢复新线程的非volatile寄存器 : : "r"(OsNext) : "r0", "r1", "sp", "memory" ); }
关于名字修饰的问题解释
你之前用asm("LDR r0,=OsCurrent");出现链接错误,确实是因为C的名字修饰:C会给全局变量/函数生成带类型信息的修饰名,汇编器找不到原始符号OsCurrent。用扩展内联汇编的输入约束"r"(OsCurrent)可以让编译器自动处理名字修饰,不需要手动写修饰后的符号,这是最可靠的解决方式。如果一定要直接在汇编里引用变量,可以给OsCurrent加extern "C"声明,关闭名字修饰:
extern "C" Thread* OsCurrent; extern "C" Thread* OsNext;
这样汇编里用LDR r0,=OsCurrent就能正确找到符号了,但更推荐用扩展内联汇编的约束方式,减少手动处理的麻烦。
内容的提问来源于stack exchange,提问作者Jean Valjean
相关产品推荐
相关产品推荐

