C++ 如何在堆栈上对齐缓冲区?

C++ 如何在堆栈上对齐缓冲区?,c++,gcc,embedded,C++,Gcc,Embedded,我试图实现一个按大小排列的内存缓冲区,这样我就可以使用DMA控制器的模功能来实现一个环形缓冲区。我知道我可以用memalign做到这一点,但我想知道是否有可能在堆栈上做到这一点,因为到目前为止,我已经能够避免动态内存。我使用的是GCC4.4.1,我不关心可移植性(嵌入式系统) 我想做一些类似的事情: template<uint16_t num_channels, uint16_t buffer_size> class sampler { __attribute__((alig

我试图实现一个按大小排列的内存缓冲区,这样我就可以使用DMA控制器的模功能来实现一个环形缓冲区。我知道我可以用memalign做到这一点,但我想知道是否有可能在堆栈上做到这一点,因为到目前为止,我已经能够避免动态内存。我使用的是GCC4.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];
};
模板
类采样器{
__属性(对齐(缓冲区大小*num\u通道*2)))
uint16_t缓冲区[缓冲区大小][通道数];
};
但当然,GCC不会接受非常量对齐(并且似乎表明对齐>8可能无论如何都不会得到遵守)

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

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


有什么想法吗?

您不需要将存储器的大小增加一倍,只需向其添加
(校准-1)
——基本上与
memalign
在幕后所做的事情相同。对于两次校准的幂:

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

您是否可以创建一个大于
buffer\u size
的缓冲区,然后计算其中的偏移量作为起点?

如果您的嵌入式系统具有内存管理单元,则无需担心动态内存的合理使用,尤其是当您每次运行只分配一次时

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


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

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

使用创建文件buffer.cpp

char buffer[ BUFFER_SIZE ];
对象文件包含名为.bss(用于未初始化数据)、.data(用于初始化数据)和.text(用于可执行代码)的部分。 缓冲区[]将进入.bss,因为它未初始化

因此,像这样的(gnu)链接器文件应该可以做到这一点

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

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

这也是我的想法,但是我认为在这种情况下对齐大小与缓冲区大小相同,因此此方法需要1小于缓冲区大小的两倍。@Chris在这种情况下,您一开始就不会浪费太多内存。实际上你什么也做不到,调用sp时,sp会有一个特定的值,最坏的情况是将
alignment-1
添加到当前值中,基本上浪费了它。您可以计算动态对齐所需的开销,然后在堆栈上动态分配数组,但仅此而已。@Voo您的意思是说“在堆上动态分配数组”吗?如果没有,你能详细说明吗?我基本上希望编译器“计算要浪费多少空间”(或者在布局上要聪明,以尽量减少浪费)。@Christopher不,我是说堆栈。堆栈上的可变长度数组是C99标准的一部分,但不是C++11的一部分。但是大多数编译器也用自己的扩展支持它们,g++当然支持(在这种情况下,VLAs不会比固定大小的数组产生更多的问题)。在某些情况下会为您节省一些内存,但在最坏的情况下,您将不得不浪费
对齐-1
内存,这是无法解决的。@Voo-Oh,我明白您的意思;我想我已经通过模板做了。因此,本质上你建议将缓冲区的大小加倍,并计算对齐。为什么你需要这样一个奇怪的对齐(缓冲区的大小)?典型的对齐需要一些基本类型(int,_m128)、页面(通常为4KB)或页面分配粒度(在Windows上通常为64KB,不知道您的控制器是如何或是否这样做的)。可能是因为它简化了DMA操作的包装逻辑。当您的内存也将被简单的硬件状态机(DMA控制器)访问时,附加的约束也会起作用。您是否考虑过将缓冲区放入其自己的转换单元中,然后使用链接器指令强制该单元的对齐或起始地址?如果你想把它放在堆栈上,我想你需要分配两倍的缓冲区大小。memalign()将使用对齐所浪费的空间来完成其他内存分配请求。这是在没有MMU的ARM Cortex M4(实际上是a)上。@brianbeuning Re:“链接器指令”我想我必须在.ld文件中选择一个缓冲区大小?如果没有,你能进一步描述吗?我的一个长期目标是生成一个供其他人使用的Arduino库,因此该模板非常有吸引力,因为用户可以在自己的项目中轻松指定大小。