我正在为微控制器编写一个内存管理器,它为池使用uint8_t
数组。从该池中,它为用户分配所请求大小的内存。
我正在研究其他内存实现。 Contiki有一个名为 mmem 。在他们的文档中,他们声明:
必须注意,使用mmem_alloc()分配的内存是1字节 对齐。这与malloc()的不同。记忆 使用malloc()分配的内容适合于每种数据类型和 返回的void指针可以安全地转换为任何其他指针 类型。
相反,指向由mmem_alloc()分配的内存的指针不能 安全地转换为除char *之外的任何指针类型,签名char *或 unsigned char *。
这意味着如果分配的内存块用于存储 结构类型的内容,结构必须声明为packed 或者必须使用memcpy()。使用GCC,可以指定压缩结构 使用以下语法:
...
所以这是一个我从未意识到的非常大的问题。
几个问题:
这适用于所有架构吗?我正在阅读Wikipedia,他们说大多数建筑都支持未对齐的数据,只会减慢速度。
(对于x68):但是,还有一些未对齐访问的指令,如MOVDQU。
Contiki链接讨论了将结构打包为解决对齐问题的方法。有没有办法去"打包"一个uint16_t
或int32_t
值?我为用户提供了用于公共数据访问的宏,因此这可能是一种可能的解决方案
ARM处理器是否支持自动未对齐访问? x86怎么样?真的有这么多陷阱吗?
我应该只允许对齐的分配请求,并填写其他任何数据吗?这对我来说非常烦人。
修改
感谢您提供有用的答案,但是由于内存管理器的工作原理(它必须通过移动数据进行碎片整理),我无法写出用户请求对齐的内容 - 我不愿意添加担心对齐的复杂性一些数据,因为这将显着影响整个系统的性能)
似乎在我的系统(Ubuntu)和我定位的ARM内核上,数据对齐不是问题。
来自here:
-munaligned-access
-mno-unaligned-access
允许(或禁用)从非16位或32位对齐的地址读取和写入16位和32位值。 默认情况下,对所有pre-ARMv6及所有ARMv6禁用未对齐访问 ARMv6-M架构,并支持所有其他架构。...
此外,以下代码可以像我们希望的那样在我的(x86_64 Ubuntu 14.04)系统上执行:
#include "stdio.h"
int main(){
char data[100];
unsigned int *value;
// Some random data
unsigned int check = (unsigned int)0x324FE23A;
// make the pointer unaligned
value = (unsigned int *)(data + 1);
*value = check;
printf("bool=%u, value=%x, check=%x\n", *value==check, *value, check);
return 0;
}
当我编译并运行它时:
$ cc playground cc align.c && ./a.out
bool=1, value=324fe23a, check=324fe23a
解决方案:
我将添加一个预编译器标志,以请求以单词形式完成请求,以便所有数据自动对齐。但是,对于我目前定位的系统,这不是必需的。
优秀问题:
如果有人知道办法:
#define tm_uint16_p(index) ((uint16_t *)tm_void_p(index))
以某种方式如果tm_void_p(index)
对齐将无关紧要,那么这将是非常好的。
注意:上面只是将索引转换为void指针,然后将其转换为uint16_t
指针。显然在某些系统上,由于对齐问题,这会失败 - 是否可以指定编译器只处理它?
答案 0 :(得分:2)
并非所有处理器都适用。
如果我正在设计低级API,那么在重要的处理器上,我会做类似的事情
void *my_alloc(size_t size, size_t align)
如果调用者正在操纵字符数组,则会传递align
为1.对于各种小类型,他们会传递sizeof(short)
,sizeof(int)
等。
然后给他们一个无能为力的分配器,其中align是MIN(roundup_to_power_of_2(size), SIZE_ON_THIS_ARCH_THATS_ALWAYS_GOOD_ENOUGH)
不,round_up_to_power_of_2
不是标准函数(或宏);-)
答案 1 :(得分:0)
一个简单的解决方案是将所需的对齐作为第二个参数传递给您编写的函数,该函数将获得指向已分配内存的指针