首页 文章

使用`__attribute __((packed))`过度使用struct包装警告?

提问于
浏览
3

我正在32位ARM mcu(Atmel SAM4SD32C,Cortex-M4 / ARMv7E-M部件)上实现二进制日志系统,并且正在设计我的数据结构 . 我的目标是将日志格式描述为打包结构,并简单地将结构与char数组结合,以便写入日志设备(SD卡,通过FatFS,在本例中) .

基本上,我有一个非常简单的结构:

typedef struct adc_samples_t
{
    int32_t adc_samples[6];

    uint64_t acq_time;

    int8_t  overrun;
    uint8_t padding_1;
    uint8_t padding_2;
    uint8_t padding_3;

} __attribute__((packed, aligned(4))) adc_sample_set;

现在,我的架构是32位,所以据我所知,访问任何成员/其他/然后 overrun 成员应该是32位对齐,因此没有额外的开销 . 此外, aligned(4) 属性应强制结构的任何实例化都在32位对齐的边界上 .

但是,编译上面的结构定义会产生一堆警告:

In file included from ../src/main.c:13:0:
<snip>\src\fs\fs-logger.h(10,10): warning: packed attribute causes inefficient alignment for 'adc_samples' [-Wattributes]
          int32_t adc_samples[6];
                  ^
<snip>\src\fs\fs-logger.h(12,11): warning: packed attribute causes inefficient alignment for 'acq_time' [-Wattributes]
          uint64_t acq_time;

据我所知(我现在意识到这是一个很大的假设),我认为32位对齐是32位臂上最佳元件定位所需要的 . 奇怪的是,唯一发出/不发出警告的成员是overrun和padding_X成员,我不明白其原因 . (好的,ARM docsByte accesses are always aligned.

究竟是什么发生在这里?我假设(可能不正确)结构实例化将在4字节边界上 . 编译器是否需要更宽的对齐(在8字节边界上)?


编辑:好的,深入研究ARM文档(这里的神奇字词是“Cortex-M4对齐”:

3.3.5 . 地址对齐对齐访问是一种操作,其中字对齐地址用于字,双字或多字访问,或者半字对齐地址用于半字访问 . 字节访问始终对齐 . Cortex-M4处理器仅支持以下指令的非对齐访问:LDR,LDRT LDRH,LDRHT LDRSH,LDRSHT STR,STRT STRH,STRHT所有其他加载和存储指令如果执行未对齐访问,则会生成UsageFault异常,因此它们的访问权限必须是地址对齐 . 有关UsageFaults的更多信息,请参阅故障处理 . 未对齐访问通常比对齐访问慢 . 此外,某些内存区域可能不支持未对齐的访问 . 因此,ARM建议程序员确保访问是一致的 . 要捕获意外生成的未对齐访问,请使用配置和控制寄存器中的UNALIGN_TRP位,请参见配置和控制寄存器 .

我的32位对齐值如何不是字对齐的?用户指南将“对齐”定义如下:

对齐存储在一个地址的数据项,该地址可以被定义数据大小的字节数整除,据说是对齐的 . 对齐的单词和半字分别具有可被4和2整除的地址 . 因此,字对齐和半字对齐的术语规定了可被4和2整除的地址 .

2 回答

  • 2

    我假设在32位ARM上进行最佳组件定位需要32位对齐

    它是 .

    但是你在这里没有32位对齐[在最初问的问题] because

    为struct和union类型指定packed属性等效于在每个结构或union成员上指定packed属性 .

    given that

    packed属性指定变量或结构字段应具有最小可能的对齐 - 变量的一个字节和字段的一个位,除非您使用aligned属性指定更大的值 .

    换句话说,如果你仍然想要一个压缩结构在你强制所有成员对齐之后仍然有一些最小对齐,因此类型本身没有任何东西,你需要指定 - 事实上这可能实际上不是make -Wpacked 闭嘴是另一回事 - GCC可能只是在它实际考虑任何进一步的对齐修饰符之前反复吐出 .

    请注意,就序列化而言,无论如何都不一定需要打包它 . 这些成员完全符合9个单词,所以唯一的编译器填充在任何地方都是一个额外的单词,最后将总大小舍入到40个字节,因为 acq_time 强制结构自然对齐为8.除非你想操作一个在这些事情的同时,你可以完全忽略它,并仍然将成员视为一个36字节的块 .

  • 4

    好的,在这一点上,我有点确信警告是错误发出的 .

    我有一个静态定义的结构实例,并且在某一点上我将其归零:

    adc_sample_set running_average;
    int accumulated_samples;
    
    inline void zero_average_buf(void)
    {
    
        accumulated_samples = 0;
    
        running_average.adc_samples[0] = 0;
        running_average.adc_samples[1] = 0;
        running_average.adc_samples[2] = 0;
        running_average.adc_samples[3] = 0;
        running_average.adc_samples[4] = 0;
        running_average.adc_samples[5] = 0;
    
        running_average.overrun = 0;
    
        running_average.acq_time = 0;
    
    }
    

    该功能的反汇编如下:

    {
    004005F8   push {r3, lr}         
        accumulated_samples = 0;
    004005FA   movs r2, #0       
    004005FC   ldr  r3, [pc, #36]        
    004005FE   str  r2, [r3]         
        running_average.adc_samples[0] = 0;
    00400600   ldr  r3, [pc, #36]        
    00400602   str  r2, [r3]         
        running_average.adc_samples[1] = 0;
    00400604   str  r2, [r3, #4]         
        running_average.adc_samples[2] = 0;
    00400606   str  r2, [r3, #8]         
        running_average.adc_samples[3] = 0;
    00400608   str  r2, [r3, #12]        
        running_average.adc_samples[4] = 0;
    0040060A   str  r2, [r3, #16]        
        running_average.adc_samples[5] = 0;
    0040060C   str  r2, [r3, #20]        
        running_average.overrun = 0;
    0040060E   strb.w   r2, [r3, #32]        
        running_average.acq_time = 0;
    00400612   movs r0, #0       
    00400614   movs r1, #0       
    00400616   strd r0, r1, [r3, #24]
    

    注意,上面的 r30x2001ef70 ,实际上是4字节对齐的 . r2 是文字值 0 .

    str 操作码需要4字节对齐 . strd 操作码也只需要4字节对齐,因为它看起来确实是两个连续的4字节操作,但我不知道它在内部是如何工作的 .


    如果我故意错误对齐我的结构,强制慢速路径复制操作:

    typedef struct adc_samples_t
    {
        int8_t  overrun;
        int32_t adc_samples[6];
    
        uint64_t acq_time;
    
    
        uint8_t padding_1;
        uint8_t padding_2;
        uint8_t padding_3;
    
    } __attribute__((packed, aligned(8))) adc_sample_set;
    

    我得到以下程序集:

    {
    00400658   push {r3, lr}         
        accumulated_samples = 0;
    0040065A   movs r3, #0       
    0040065C   ldr  r2, [pc, #84]        
    0040065E   str  r3, [r2]         
        running_average.adc_samples[0] = 0;
    00400660   ldr  r2, [pc, #84]        
    00400662   strb r3, [r2, #1]         
    00400664   strb r3, [r2, #2]         
    00400666   strb r3, [r2, #3]         
    00400668   strb r3, [r2, #4]         
        running_average.adc_samples[1] = 0;
    0040066A   strb r3, [r2, #5]         
    0040066C   strb r3, [r2, #6]         
    0040066E   strb r3, [r2, #7]         
    00400670   strb r3, [r2, #8]         
        running_average.adc_samples[2] = 0;
    00400672   strb r3, [r2, #9]         
    00400674   strb r3, [r2, #10]        
    00400676   strb r3, [r2, #11]        
    00400678   strb r3, [r2, #12]        
        running_average.adc_samples[3] = 0;
    0040067A   strb r3, [r2, #13]        
    0040067C   strb r3, [r2, #14]        
    0040067E   strb r3, [r2, #15]        
    00400680   strb r3, [r2, #16]        
        running_average.adc_samples[4] = 0;
    00400682   strb r3, [r2, #17]        
    00400684   strb r3, [r2, #18]        
    00400686   strb r3, [r2, #19]        
    00400688   strb r3, [r2, #20]        
        running_average.adc_samples[5] = 0;
    0040068A   strb r3, [r2, #21]        
    0040068C   strb r3, [r2, #22]        
    0040068E   strb r3, [r2, #23]        
    00400690   strb r3, [r2, #24]        
        running_average.overrun = 0;
    00400692   mov  r1, r2       
    00400694   strb r3, [r1], #25        
        running_average.acq_time = 0;
    00400698   strb r3, [r2, #25]        
    0040069A   strb r3, [r1, #1]         
    0040069C   strb r3, [r1, #2]         
    0040069E   strb r3, [r1, #3]         
    004006A0   strb r3, [r1, #4]         
    004006A2   strb r3, [r1, #5]         
    004006A4   strb r3, [r1, #6]         
    004006A6   strb r3, [r1, #7]
    

    所以,非常清楚,我正在使用我原来的结构定义获得正确的对齐复制行为, despite 编译器显然错误地警告它会导致访问效率低下 .

相关问题