良许Linux教程网 干货合集 嵌入式软件中,总线错误的坑?替大家先踩一步

嵌入式软件中,总线错误的坑?替大家先踩一步

最近我遇到了一个总线错误的问题,现在我来简单总结并分享给大家。

什么是总线错误?

在平时的开发过程中,我们常常遇到引起进程崩溃的错误,其中最常见的就是段错误(segmentation fault)。段错误是指当程序访问的内存超出了系统所分配给该程序的内存空间时,比如操作空指针、数组越界等情况。

与段错误类似,总线错误(Bus Error)是因为对非对齐地址进行访问而导致CPU违反了一定的总线规则。

为了保证CPU的性能,要求对数据进行访问时必须保证地址对齐。当发现进行了非对齐的访问时,CPU会向进程发送SIGBUS信号,导致进程产生核心转储(core dump)。

总线错误与CPU架构有关,某些架构的CPU支持非对齐访问。接下来,我将通过一个实例来进行分析:

总线错误的实例

#include 
#include 

#pragma pack(1)
struct struct_x
{
    char a;
    float b;
    char c;
};
#pragma pack()

int main(void)
{
    struct struct_x test = {0};

    printf("sizeof(struct struct_x) = %ld\n", sizeof(test));

    test.a = 1;
    test.b = 2.0;
    test.c = 3;

    char *a = &test.a;
    float *b = &test.b;
    char *c = &test.c;

    printf("*a = %d, addr = %p\n", *a, a);
    printf("*b = %f, addr = %p\n", *b, b);
    printf("*c = %d, addr = %p\n", *c, c);

    return 0;
}

#pragma pack 可以改变编译器的对齐方式:

#pragma pack(n)  /* 指定按n字节对齐 */
#pragma pack()   /* 取消自定义字节对齐 */

在pc端,可以正常运行:

image-20230904210247310
image-20230904210247310

因为x86/x64系列CPU都支持不对齐访问,也提供了开关禁用这个机制。x86/x64架构不要求对齐访问的时候,必定会有性能代价。

但是,在arm板上测试:

image-20230904210250512
image-20230904210250512
image-20230904210253200
image-20230904210253200

出现了总线错误,因为结构体变量test的成员b的地址是不对齐的地址。CPU访问地址要求是四字节对齐,访问了*(addr+0x001)就会引发异常。

这时候,在struct_x的成员a、b之前增加个占用3个字节的成员d,看看还会不会报错:

struct struct_x
{
    char a;
    char d[3];
    float b;
    char c;
};
image-20230904210256731
image-20230904210256731

可见,成员b可以正常访问,因为这时候b的地址处于四字节对齐地址。

上面的总线错误,毫无疑问,就是对齐问题导致的。

但是,这里有个疑问。假如,我们把成员b的类型改为int类型,这时候会不会产生总线错误?

#include 
#include 

#pragma pack(1)
struct struct_x
{
    char a;
    int b;
    char c;
};
#pragma pack()


int main(void)
{
    struct struct_x test = {0};

    printf("sizeof(struct struct_x) = %ld\n", sizeof(test));

    test.a = 1;
    test.b = 2;
    test.c = 3;

    char *a = &test.a;
    int *b = &test.b;
    char *c = &test.c;

    printf("sizeof(float) = %d, sizeof(int) = %d\n", sizeof(float), sizeof(int) );
    printf("*a = %d, addr = %p\n", *a, a);
    printf("*b = %d, addr = %p\n", *b, b);
    printf("*c = %d, addr = %p\n", *c, c);

    return 0;
}
image-20230904210300410
image-20230904210300410

这里的int类型的b成员可以正常访问。这里的成员b的地址与我们上面发生总线错误的b的成员(float类型)的地址完全一样,float类型与int类型也都是占用4字节,但是int类型b成员却可以支持非对齐访问。

这里,暂时就认为CPU就是这么设计的吧。能解释这个问题的朋友欢迎留言讨论,谢谢!

总结

上面的int类型的b成员虽然可以正常访问,但是我们在实际编程中,应当多注意一点,尽量要修改对齐方式。

如果确实需要,也尽量保证修改的对齐方式的代码范围尽量小,比如只针对某个结构体,并且清楚地知道有这么一回事,以至于后面加代码的时候需要非常地小心。

以上是本次的分享,如果觉得文章有帮助,麻烦帮忙转发,谢谢大家!

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部