介绍
能够对齐或不对齐的内存拜访。对齐的内存拜访产生时的数据都坐落其天然巨细鸿沟。例如,假如该数据类型的巨细是4个字节,那么它归于被4整除的内存地址是坐落其天然巨细鸿沟。未对齐的内存拜访产生在一切其他情况下(在上面的比如中,内存地址时,是不能被4整除)。ARM处理器的规划有效地拜访对齐的数据。在ARM处理器上企图拜访未对齐的数据会导致不正确的数据或明显的功用丢失(这些不同的症状会在稍后评论)。与此相反,大多数CISC型处理器(即x86)的拜访未对齐的数据是无害的。这份文件将评论一些比较常见的方法,一个应用程序或许会履行未对齐的内存拜访,并供给一些主张的处理方案,以防止这些问题, 。
症状
上述问题,适用于一切ARM架构。可是,依据MMU(内存办理单元)和操作系统支撑的可用性,应用程序或许会看到不同的行为在不同的平台上。默许情况下,未对齐的内存拜访不会被困住了,会导致不正确的数据。与功用的MMU的平台上,可是,OS捕获非对齐拜访,它在运行时进行纠正。其成果将是正确的数据,但在10-20 CPU周期的本钱。
常见原因
上述问题的类型转化适用于一切ARM架构。可是,依据MMU(内存办理单元)和操作系统支撑的可用性,应用程序或许会看到不同的行为在不同的平台上。默许情况下,未对齐的内存拜访不会被困住了,会导致不正确的数据。与功用的MMU的平台上,可是,OS捕获非对齐拜访,它在运行时进行纠正。其成果将是正确的数据,但在10-20 CPU周期的本钱。
代码:
void my_func(char *a) { int *b = (int *)a; DBGPRINTF("%d", *b); }
这个简略的比如,或许会导致未对齐的内存拜访,因为咱们不能保证的char * a是一个4字节的鸿沟上对齐。只需有或许,应防止这种类型的施放。
运用数据缓冲区
未对齐的内存拜访的最常见的原因源于不正确地处理数据缓冲区。这些数据缓冲区或许包括任何数据从USB端口读取,经过网络,或从一个文件中。这个数据是很常见的包装,有没有刺进填充,以保证数据在缓冲区内坐落其天然巨细鸿沟。在这个比如中,咱们会考虑的情况下,从文件加载的Windows BMP和解析的头。的Windows BMP文件包括一个头的像素数据。的标头是由两个结构:
代码:
typedef PACKED struct { unsigned short int type; /* Magic identifier */ unsigned int size; /* File size in bytes */ unsigned short int reserved1, reserved2; unsigned int offset; /* Offset to image data, bytes */ } HEADER; typedef PACKED struct { unsigned int size; /* Header size in bytes */ int width,height; /* Width and height of image */ unsigned short int planes; /* Number of colour planes */ unsigned short int bits; /* Bits per pixel */ unsigned int compression; /* Compression type */ unsigned int imagesize; /* Image size in bytes */ int xresolution,yresolution; /* Pixels per meter */ unsigned int ncolours; /* Number of colours */ unsigned int importantcolours; /* Important colours */ } INFOHEADER;
请留意,在的HEADER和INFOHEADER结构的巨细,分别为14和40字节。让咱们假定咱们要确认在运行时的图画的宽度和高度。的代码来拜访这些数据或许看起来像这样:
代码:
#define INFOHEADER_OFFSET (sizeof(HEADER)) #define WIDTH_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, width)) #define HEIGHT_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, height)) int imageWidth, imageHeight; void * fileBuf; pMe->mFile = IFILEMGR_OpenFile(pMe->mFileMgr, "test.bmp", _OFM_READ); if (pMe->mFile) { IFILE_GetInfo(pMe->mFile, &fileInfo); fileBuf = MALLOC(fileInfo.dwSize); if (fileBuf) { result = IFILE_Read(pMe->mFile, fileBuf, fileInfo.dwSize); if (result == fileInfo.dwSize) { imageWidth = *((uint32*)(((byte*)fileBuf) + WIDTH_OFFSET)); imageHeight = *((uint32*)(((byte*)fileBuf) + HEIGHT_OFFSET)); } } }
留意的宽度和高度的偏移量。因为他们归于一个半字鸿沟上,以上述方法拜访这些值会导致未对齐的内存拜访。下面列出的一些引荐的方法来防止这个问题。
引荐的处理方案
运用memcpy
咱们的第一个选项是,只需履行MEMCPY从缓冲区中的数据到本地变量:
代码:
if (result == fileInfo.dwSize) { MEMCPY(&imageWidth, (((byte*)fileBuf)+WIDTH_OFFSET), sizeof(uint32)); MEMCPY(&imageHeight, (((byte*)fileBuf)+HEIGHT_OFFSET), sizeof(uint32)); }
其成果是,存储器被字节逐字节,防止任何疑问对准。
包装的编译器指令
或许,咱们能够运用紧缩的编译器指令答应运用指针,直接将咱们需求的数据,一起迫使编译器来处理对齐问题。在BREW环境中,PACKED被界说如下:
代码:
#ifdef __ARMCC_VERSION #define PACKED __packed #else #define PACKED #endif
包装方法,经过指定一个指针,ARM编译器将生成相应的说明来正确地拜访内存,不管对齐。修改后的版别,上面的比如中,运用PACKED指针,如下:
代码:
#define INFOHEADER_OFFSET (sizeof(HEADER)) #define WIDTH_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, width)) #define HEIGHT_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, height)) PACKED uint32 * pImageWidth; PACKED uint32 * pImageHeight; uint32 imageWidth, imageHeight; void * fileBuf; pMe->mFile = IFILEMGR_OpenFile(pMe->mFileMgr, "test.bmp", _OFM_READ); if (pMe->mFile) { IFILE_GetInfo(pMe->mFile, &fileInfo); fileBuf = MALLOC(fileInfo.dwSize); if (fileBuf) { result = IFILE_Read(pMe->mFile, fileBuf, fileInfo.dwSize); if (result == fileInfo.dwSize) { pImageWidth = (uint32*)(((byte*)fileBuf) + WIDTH_OFFSET); pImageHeight = (uint32*)(((byte*)fileBuf) + HEIGHT_OFFSET); imageWidth = *pImageWidth; imageHeight = *pImageHeight; } } }
尽管程序员通常会无法控制标准化的数据格式,如BMP头在上面的比如中,当你界说自己的数据结构应保证奠定了杰出的对齐方法中的数据界说对齐的数据结构。下面的根本示例演示了这样的准则:
代码:
#ifdef __ARMCC_VERSION typedef PACKED struct { short a; // offsetof(a) = 0 int b; // offsetof(b) = 2 � misalignment problem! short c; // offsetof(c) = 6 } BAD_STRUCT; typedef struct { int b; // offsetof(b) = 0 � no problem! short a; // offsetof(a) = 4 short c; // offsetof(c) = 6 } GOOD_STRUCT;
经过简略地重新排列中,咱们声明的结构成员,咱们能够处理一些对齐的问题。别的请留意,假如未声明为包装,BAD_STRUCT,编译器通常会刺进填充,每个字段对齐。可是,这通常是不期望的,因为它糟蹋内存和防止简直总是能够简略地经过声明为了减小尺度的字段。
BREW模拟器测验
BREW模拟器3.1.2及以上版别供给了能够使数据对齐查看。BREW模拟器启用此功用时,将显现一个对话框,告诉您的每一个未对齐的内存拜访,并为您供给的选项对这一问题视若无睹,或闯入的代码,请参阅BREW SDK用户文档一节揗isaligned数据反常支撑更多信息,此功用。注:因为x86架构的拜访未对齐的数据不会有任何问题,你能够不编译模拟器的DLL运用__packed指令(PACKED这便是为什么在WIN32环境下的空白被界说为)。这意味着,经过运用PACKED指针的非对齐拜访,处理依旧会触发在模拟器的对齐查看。