0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

调试 RT-Thread 时,我的代码怎么“被优化”了? | 技术集结

RT-Thread官方账号 2025-10-18 10:34 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

以下是关于volatile关键的外文描述:

By declaring an object volatile, the compiler is informed that the value of the object can change beyond the compiler’s control. The compiler must also assume that any accesses can have side effects—thus all accesses to the volatile object must be preserved.

There are three main reasons for declaring an object volatile:

Shared access; the object is shared between several tasks in a multitasking environment

Trigger access; as for a memory-mapped SFR where the fact that an access occurshas an effect

Modified access; where the contents of the object can change in ways not known tothe compiler.

1、Shared access

the object is shared between several tasks in a multitasking environment。

当同一全局变量在多个线程之间被共享时,有可能会出现同步错误,编译器可能会将访问该全局变量的代码优化为访问某个寄存器,而不会再次访问相应的内存,导致程序运行错误。

测试代码如下:

staticstructrt_threadv_thread1;
staticcharv_thread1_stack[8192];
staticstructrt_threadv_thread2;
staticcharv_thread2_stack[8192];

staticintflag;
staticintcount;

staticvoidrt_init_thread1_entry(void*parameter)
{
while(1)
{
rt_thread_mdelay(300);
flag=1;
rt_thread_mdelay(300);
flag=0;

if(count++>10)
{
rt_kprintf("thread1 exit.\n");
flag=1;
return;
}
}
}

staticvoidrt_init_thread2_entry(void*parameter)
{
while(1)
{
while(flag==0);
rt_kprintf("thread2 running.\n");
rt_thread_mdelay(100);

if(count++>10)
{
rt_kprintf("thread2 exit.\n");
return;
}
}
}

intvolatile_test()
{
rt_err_tresult=RT_EOK;
result=rt_thread_init(&v_thread1,"vth1",
rt_init_thread1_entry,
RT_NULL,
v_thread1_stack,sizeof(v_thread1_stack),
RT_THREAD_PRIORITY_MAX/3-1,20);
if(result==RT_EOK)
rt_thread_startup(&v_thread1);

result=rt_thread_init(&v_thread2,"vth2",
rt_init_thread2_entry,
RT_NULL,
v_thread2_stack,sizeof(v_thread2_stack),
RT_THREAD_PRIORITY_MAX/3,20);
if(result==RT_EOK)
rt_thread_startup(&v_thread2);

return0;

}
MSH_CMD_EXPORT(volatile_test,runvolatile_test);

上面的测试代码在 O0 优化时正常运行,打印结果如下:

msh />volatile_test
thread2 running.
msh />thread2 running.
thread2 running.
thread2 running.
thread2 running.
thread2 running.
thread2 running.
thread2 running.
thread2 running.
thread2 exit.
thread1 exit.

但是如果开启 O3 优化,则打印结果如下:

msh />volatile_test
thread1 exit.

也就是说 thread2 永远得不到运行,那么原因是什么呢,请看下图的反汇编,语句

while(flag==0);

被优化成了如下汇编:

00108b4c: ldr r3, [r4, #+288] # 第一次读取 flag 的实际值到 r3
00108b50: cmp r3,#0 # 对比 r3 的值是否为 0
00108b54: bne +0 ; # 如果不为 0 则跳转
00108b58: b -8 ; # 再次跳转回 cmp 语句继续循环

也就是说,整个程序被翻译成,只读取一次 flag 的实际值,后续一直使用 r3 寄存器中的值来进行对比,而第一次读取到的 r3 值为零,因此 while 的条件将永远成立,thread2 永远也得不到执行。

2、Trigger access

as for a memory-mapped SFR(特殊功能寄存器)where the fact that an access occurs has an effect。

当读取类似串口设备的数据寄存器时,一定要加上 volatile,因为该地址寄存器中的数值可能会发生改变,如果不加 volatile,可能会发现读取的数据是错误的。

3、Modified access

where the contents of the object can change in ways not known to the compiler.

对象的内容可能会被以编译器不清楚的方式被修改,例如在内核态与用户态的程序在不同的虚拟地址访问同一块物理内存,此时如果不加上 volatile,则外部的修改无法被感知到,造成程序错误。

关于优化错误

如果系统在低优化等级能正常运行,但是在高优化的情况下的无法正常运行,首先怀疑两个方面:

是否是一些关键操作没有添加 volatile

是否是有内存写穿(因为不同的优化等级改变了内存排布导致写穿位置发生改变)

4、如何避免关键操作被优化

情况一

如果发现加上了printf打印,或者调用了某个外部函数,系统就正常运行了,也要怀疑是否出现了变量访问被优化的情况,因为如果加上了外部函数(非本文件中的函数或其他库中的函数)调用,则编译器无法确定被引用的变量是否被外部函数所改变,因而会自动从原有地址重新读取该变量的值。

如果修改上面的测试代码,在 while 循环中加入rt_kprintf打印如下:

while(flag==0)
{
rt_kprintf("5\n");
}

则程序仍然正常运行,原因就是编译器不知道rt_kprintf函数是否会修改 flag 变量,因此编译器会尝试每次都重新读取flag的值。

情况二

还可以使用另外一种方式来解决这个问题,如下:

while(flag==0)
{
asmvolatile("":::"memory");
}

If our instruction modifies memory in an unpredictable fashion, add "memory" to the list of clobbered registers. This will cause GCC to not keep memory values cached in registers across the assembler instruction. We also have to add thevolatile keywordif the memory affected is not listed in the inputs or outputs of the asm.

这将会告诉编译器,经过一些指令后,memory 中的数据已经发生了变化,GCC 将不会再使用寄存器作为数据的缓存。因此再次使用这些数据时,会从内存中重新尝试读取。使用关键字 volatile 也可以达到同样的效果。

以下描述摘自《GCC-Inline-Assembly-HOWTO》:

Some instructions clobber some hardware registers. We have to list those registers in the clobber-list, ie the field after the third ’:’ in the asm function. This is to inform gcc that we will use and modify them ourselves. So gcc will not assume that the values it loads into these registers will be valid. We shoudn’t list the input and output registers in this list. Because, gcc knows that "asm" uses them (because they are specified explicitly as constraints). If the instructions use any other registers, implicitly or explicitly (and the registers are not present either in input or in the output constraint list), then those registers have to be specified in the clobbered list.

If our instruction can alter the condition code register, we have to add "cc" to the list of clobbered registers.

4、结论

关于 volatile 关键字,最重要的是要认识到一点,即是否在编译器清楚的范围之外,所操作的变量有可能被改变,如果有这种可能性,则一定要添加上 volatile 关键字,以避免这种错误。

归根结底,是要确定代码在真实运行的状态下,当其访问某个变量时,是否真正地从这个变量所在的地址重新读取该变量的值,而不是直接使用上次存储在某个寄存器中的值。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5590

    浏览量

    129103
  • 代码
    +关注

    关注

    30

    文章

    4942

    浏览量

    73166
  • RT-Thread
    +关注

    关注

    32

    文章

    1544

    浏览量

    44301
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    【原创精选】RT-Thread征文精选技术文章合集

    RT-Thread技术原创文章精选合集NO1 专栏作者 :矜辰所致简介:不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!画画板子敲敲代码玩玩RTOS搞搞Linux。1. STM32L051
    发表于 07-26 14:56

    RT-Thread Smart已正式上线,源代码可下载,采用Apache License 2.0

    官方消息,RT-Thread Smart已正式上线,目前源代码可在Github、Gitee下载,采用Apache License 2.0。 源码下载链接: https://github.com
    的头像 发表于 11-26 14:49 3485次阅读

    记录——基于 RT-Thread 实现 USB 虚拟串口

    记录——基于 RT-Thread 实现 USB 虚拟串口记录一下在正点原子 F429 阿波罗开发板上实现 USB 虚拟串口的过程,希望可以帮助到其他想要学习 USB 的人。首先,更新
    发表于 12-02 17:21 6次下载
    记录——基于 <b class='flag-5'>RT-Thread</b> 实现 USB 虚拟串口

    RT-Thread全球技术大会:萤石研发团队使用RT-Thread技术挑战

    RT-Thread全球技术大会:研发团队使用RT-Thread技术挑战         审核编辑:彭静
    的头像 发表于 05-27 11:36 1870次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:萤石研发团队使用<b class='flag-5'>RT-Thread</b>的<b class='flag-5'>技术</b>挑战

    RT-Thread全球技术大会:RT-Thread Smart更好的兼容Linux生态

    RT-Thread全球技术大会:RT-Thread Smart更好的兼容Linux生态             审核编辑:彭静
    的头像 发表于 05-27 14:31 2353次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:<b class='flag-5'>RT-Thread</b> Smart更好的兼容Linux生态

    RT-Thread全球技术大会:Kconfig在RT-Thread中的工作机制

    RT-Thread全球技术大会:Kconfig在RT-Thread中的工作机制               审核编辑:彭静
    的头像 发表于 05-27 14:49 2149次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:Kconfig在<b class='flag-5'>RT-Thread</b>中的工作机制

    RT-Thread全球技术大会:在RT-Thread上编写测试用例

    RT-Thread全球技术大会:在RT-Thread上编写测试用例           审核编辑:彭静
    的头像 发表于 05-27 16:28 1966次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:在<b class='flag-5'>RT-Thread</b>上编写测试用例

    RT-Thread全球技术大会:RT-Thread测试用例集合案例

    RT-Thread全球技术大会:RT-Thread测试用例集合案例           审核编辑:彭静
    的头像 发表于 05-27 16:34 2662次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:<b class='flag-5'>RT-Thread</b>测试用例集合案例

    RT-Thread全球技术大会:RT-Thread对POSIX的实现情况介绍

    RT-Thread全球技术大会:RT-Thread对POSIX的实现情况介绍             审核编辑:彭静
    的头像 发表于 05-27 16:52 2388次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:<b class='flag-5'>RT-Thread</b>对POSIX的实现情况介绍

    RT-Thread全球技术大会:在RT-Thread中使用栈帧来调试程序

    百问网科技CTO韦东山,在RT-Thread全球技术大会大会中,以在RT-Thread中使用栈帧来调试程序为主题进行了介绍。
    的头像 发表于 05-28 09:33 2380次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:在<b class='flag-5'>RT-Thread</b>中使用栈帧来<b class='flag-5'>调试</b>程序

    RT-Thread 编程风格

    作者:架构师李肯 前言 最近博主在学习 RT-Thread 这个开源项目,开始慢慢学习和理解它的开源代码,慢慢开始接触它的代码规范。
    的头像 发表于 12-06 19:55 1823次阅读

    RT-Thread文档_RT-Thread 简介

    RT-Thread文档_RT-Thread 简介
    发表于 02-22 18:22 5次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> 简介

    RT-Thread文档_RT-Thread SMP 介绍与移植

    RT-Thread文档_RT-Thread SMP 介绍与移植
    发表于 02-22 18:31 9次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> SMP 介绍与移植

    使用RT-Thread Studio进行智能家居终端的设计

    本次方案基于星火一号开发板开发,使用RT-Thread Studio进行工程创建,代码编辑,RT-Thread配置,调试配置,程序下载等功能。
    的头像 发表于 07-28 16:41 2781次阅读
    使用<b class='flag-5'>RT-Thread</b> Studio进行智能家居终端的设计

    RT-Thread v5.0.2 发布

    RT-Thread 代码仓库地址: ●  https://github.com/RT-Thread/rt-thread RT-Thread
    的头像 发表于 10-10 18:45 2685次阅读
    <b class='flag-5'>RT-Thread</b> v5.0.2 发布