我夏了夏天
我夏了夏天 - 认证专家
Life isn't about finding yourself, life is about creating yourself.

注册于 4 years ago

回答
77
文章
23
关注者
15

在宏内核版本中,signal 的机制还不够完善,这一定程度上是由于没有用户态导致的,所以在目前版本的宏内核中,我认为 signal 还有待完善。

在更新软件包的时候可能是网络的问题,超级超级慢是被杀毒软件阻止了么?

你需要基于你自己使用的芯片重新做一个 bsp,或者完全替换 cubemx 里的配置为新的芯片。

是的,我的域名过期了没有续期 = =

有点奇怪呀,换个 BSP 试试呢?

目前脚本里应该是没有支持这种多 target 的用法吧

不是内存溢出,而是内存不够了,给 mpy 的堆内存分配大一点吧。

如果系统配有 MMU 的话,可以配置某一块内存的属性为 uncache,那么 CPU 访问这块内存地址的时候,将不会使用 cache 中的数据作为替代,而是重新访问该地址的内存,再次读取一下该物理地址上的数据。

为了简单说明为什么需要 cache,我举一个例子,CPU 执行一条运算指令,只需要一个或者几个时钟周期,但是如果 CPU 想要从内存中读取一个数据, 则要花费上百个时钟周期,为了降低 CPU 的访存开销,在 CPU 中引入了 cache 机制。访问 CPU 缓存要比直接访问物理内存快的多,可能只需要几个时钟周期。

如果有帮助,记得选为最佳答案呦。

我看你的代码有两个方面比较可疑:

  1. 相当多线程的栈设置的非常小,而且 lift tick 数值又非常大,很容易让人怀疑为栈溢出造成的异常现象,推荐楼主仔细检查 Tflusamp 线程的栈是否够用,加大该线程的栈后再尝试运行。
  2. 贴出出错时的代码让大家看一下,仔细检查一下系统 fault 之前 memcpy 的源地址和目标地址是否是四字节对齐的,如果发生栈溢出,导致 memcpy 的源地址和目标地址被修改,那么会导致非对齐访问的 hardfault。

如果想在宏内核版本的 RT-Thread 中进行浮点运算,就需要在移植时支持浮点,并且在编译和链接的时候开启浮点支持选项。

移植时处理浮点的问题,至少要处理两种情况:

  1. 只是发生中断,而不进行线程切换时,只需要在发生中断时,将所有的寄存器(包括浮点寄存器) push 到中断栈中,在中断退出时,从中断栈中恢复先前线程上下文的寄存器组。
  2. 如果是需要进行线程切换的情况,发生中断的时候,需要将所有的寄存器(包括浮点寄存器) push 到中断栈中,然后进行中断处理,最后在切换到新线程之前,将保存在中断栈中的当前线程的 context 拷贝到线程的栈的对应位置中。

这两者的区别是,第一种情况将中断栈里保存的上下文直接恢复到寄存器组里,第二种情况是将在中断栈里保存的上下文再次保存到当前线程栈中对应的位置上。

如果上述说明在移植时都没有处理的话,那么一旦出现线程切换,浮点寄存器组中的数据将被破坏,也就是说,你的硬件浮点运算结果将会无效。你给出的代码只是浮点支持的一部分,表示线程的栈帧中是否保存浮点相关寄存器,答案是必须的,只有对浮点寄存器进行很好的保存和恢复,才能保证计算任务结果正确。

如果认为该答案解决了你的问题,请选为最佳答案呦!

对于你提出的三种规则,我重新整理了一下,将我的看法发给你作为参考:

Shared access

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

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

测试代码如下:

static struct rt_thread v_thread1;
static char v_thread1_stack[8192];
static struct rt_thread v_thread2;
static char v_thread2_stack[8192];

static int flag;
static int count;

static void rt_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;
        }
    }
}

static void rt_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;
        }
    }
}

int volatile_test()
{

    rt_err_t result = 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);

    return 0;

}
MSH_CMD_EXPORT(volatile_test, run volatile_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 永远也得不到执行。

1606203190756.png

Trigger access

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

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

Modified access

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

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

关于优化错误

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

  • 是否是一些关键操作没有添加 volatile
  • 是否是有内存写穿(因为不同的优化等级改变了内存排布导致写穿位置发生改变)

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

结论

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

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

上面的排版可能会有些乱,你可以参考我写的一个小文档

《volatile 的使用说明》来查看。

挂掉的现场有保存吗?挂掉是什么现象?

回到
顶部

发布
问题

投诉
建议