【问题标题】:How can I align a buffer on the stack?如何对齐堆栈上的缓冲区?
【发布时间】:2012-12-01 21:26:59
【问题描述】:

我正在尝试实现一个与其大小对齐的内存缓冲区,这样我就可以使用 DMA 控制器的模功能来实现一个环形缓冲区。我知道我可以用 memalign 做到这一点,但我想知道它是否可以在堆栈上这样做,因为到目前为止我已经能够避免动态内存。我使用的是 GCC 4.4.1,我不关心可移植性(嵌入式系统)。

我想做这样的事情:

template<uint16_t num_channels, uint16_t buffer_size>
class sampler {
    __attribute__((aligned(buffer_size * num_channels * 2)))
    uint16_t buffer[buffer_size][num_channels];
};

但当然 GCC 不会接受非常量对齐(并且似乎表明对齐 > 8 可能无论如何都不会被尊重)。

我想我可以使用 C++0x alignas() 来实现这一点,但它似乎直到 4.8 版才出现在 GCC 中。

我想一个选项可能是将缓冲区的大小加倍,但这似乎会浪费大量空间(我计划尝试将大部分设备内存用于此缓冲区)。也许我应该放弃并使用动态内存。 memalign 在空间浪费方面会相对有效吗?

有什么想法吗?

【问题讨论】:

  • 为什么需要这么奇怪的对齐方式(缓冲区的大小)?需要的典型对齐方式是针对某些基本类型 (int,__m128)、页面(通常为 4KB)或页面分配粒度(在 Windows 上通常为 64KB,不知道您的控制器如何或是否这样做)。
  • 可能是因为它简化了 DMA 操作的包装逻辑。当您的内存也将被作为 DMA 控制器的简单硬件状态机访问时,额外的约束就会发挥作用。
  • 您是否考虑过将缓冲区放在自己的翻译单元中,然后使用链接器指令强制对齐或该单元的起始地址?如果你想要它在堆栈上,我认为你需要分配两倍的缓冲区大小。 memalign() 将使用对齐浪费的空间来满足其他内存分配请求。
  • 这是在没有 MMU 的 ARM Cortex M4(实际上是 teensy 3)上。
  • @brianbeuning 回复:“链接器指令”我想我必须在 .ld 文件中选择一个缓冲区大小?如果没有,您能进一步描述吗?我的长期目标之一是制作一个可供其他人使用的 Arduino 库,因此该模板非常有吸引力,因为用户可以轻松地在自己的项目中指定大小。

标签: c++ gcc embedded


【解决方案1】:

您不需要将存储空间的大小增加一倍,您只需在其中添加(alignment - 1) —— 基本上与memalign 在幕后所做的相同。对于两个对齐的幂:

char buf[size + (alignment -1)];
char *aligned = (char*)((intptr_t)buf + (alignment - 1) & ~intptr_t(alignment - 1));

【讨论】:

  • 这也是我的想法,但是我认为在这种情况下对齐大小与缓冲区大小相同,因此这种方法需要小于两倍的缓冲区大小。
  • @Chris 在这种情况下,您并没有真正浪费太多内存。而且您确实无能为力,sp 将在您调用它时具有特定值,最坏的情况意味着将alignment - 1 添加到当前值并基本上浪费它。您可以计算出动态对齐需要浪费多少,然后在堆栈上动态分配数组,仅此而已。
  • @Voo 你的意思是说“在堆上动态分配数组”吗?如果没有,你能详细说明吗?我基本上确实希望编译器“计算要浪费多少空间”(或者巧妙地放置以最大程度地减少浪费)。
  • @Christopher 不,我的意思是堆栈。堆栈上的可变长度数组是 C99 标准的一部分,但不是 C++11 的一部分。但是大多数编译器也通过自己的扩展支持它们,g++ 肯定支持(在这种情况下,VLA 不会比固定大小的数组已经产生更多问题)。在某些情况下会为您节省一些内存,但在最坏的情况下,您将不得不浪费alignment - 1 内存,这无法解决。
  • @Voo 哦,我明白你的意思了;我想我已经通过模板这样做了。所以基本上你建议将缓冲区的大小加倍并计算对齐。
【解决方案2】:

我已经很久没有使用链接器命令文件了,但我认为它会是这样的。

创建文件buffer.cpp
char buffer[ BUFFER_SIZE ];

对象文件具有名为 .bss(用于未初始化数据)、.data(用于初始化数据)和 .text(用于可执行代码)的部分。 buffer[] 将进入 .bss,因为它没有被初始化。

所以像这样的(gnu)链接器文件应该可以解决问题

SECTIONS {
   .bss 0x0  : {
        buffer.o(.bss)
        *(.bss)
    }
   .data : {
        *(.data)
    }
   .text : {
        *(.text)
    }
}

0x0 告诉链接器在地址 0x0 加载缓冲区[]。

【讨论】:

    【解决方案3】:

    您能否创建一个大于buffer_size 的缓冲区,然后计算其中的偏移量以从哪里开始?

    【讨论】:

      【解决方案4】:

      如果您的嵌入式系统具有内存管理单元,则无需担心明智地使用动态内存,尤其是在每次运行只分配一次的情况下。

      如果它没有 MMU,您可以考虑使用链接器映射文件分配一个固定位置。

      在具有实际操作系统的系统上,与 DMA 兼容的缓冲区可能必须由内核专门分配。

      【讨论】:

        猜你喜欢
        • 2011-09-08
        • 1970-01-01
        • 2017-02-23
        • 2012-11-23
        • 1970-01-01
        • 2021-12-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多