注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

指南针的天空

你永远也看不到我最寂寞时候的样子,因为只有你不在我身边的时候,我才最寂寞。

 
 
 

日志

 
 

ARM处理器下的内存对齐问题  

2011-11-22 22:39:52|  分类: 嵌入式&ARM |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

from:
Memory Alignment Issues on ARM Processors
https://brewx.qualcomm.com/bws/content/gi/common/appseng/en/knowledgebase/docs/kb95.html

介绍
    内存访问可以分为aligned和未对齐unaligned.对齐内存访问发生在数据分配在natural size boundary,如果这个数据的大小是4 bytes,而且它分配的地址可以被4整除,它就是分配在natural size boundary的,它就是内存对齐的.未对齐内存访问就是其他的所有情况(内存地址不能被4整除);
    ARM处理器被设计成可以高效的访问对齐数据,在ARM处理器上尝试访问未对齐内存数据将得到两种结果:错误数据或显著的执行差异(很快会讨论这些不同的表现).这不同于其他的CISC类型的处理器,它们可以正常的访问未对齐数据.
    这篇文档将会描述一些对应用程序来说通用的方式处理未对齐内存访问和提供一些推荐的解决方案以解决这些问题.
   
症状

    上述问题针对所有ARM架构的.然而,根据MMU是否使能和操作系统的支持,应用程序在不同的平台上有不同的表现.在默认情况下,未对齐内存访问不会被捕 捉,而是返回一个错误数据.在使能了MMU的平台上,OS将可以捕捉未对齐内存访问而且在运行时调整正确.返回的结果将是正确的数据,但是将花费 10-20个cpu周期.

通常原因

类型分配

Code:

void my_func(char* a)

{

    int *b = (int*)a;

    DBGPRINTF("%d",*b);

}

    这个简单的例子可以生成未对齐内存访问,因为我们不能保证参数char* a是在4字节边界上的.这样的类型定义在任何时候都应该避免.

使用数据buffer


    大多数常见的未对齐内存访问发生在错误的处理数据buffer,这些数据buffer可能包含任何从usb端口,网路,或文件中读取的数据.通常会设置这 些数据为packed,意味着没有padding嵌入以确保buffer中的数据是natural size boundary的.在这个例子中,我们将讨论装载从一个文件一个windows BMP格式数据,然后解析其文件头的情况.
    一个windows BMP文件包含一个以下数据项的文件头,文件头由两个结构体组成:


Code:

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字节.

假设我们想在程序运行是检测图片的宽带和高度,得到这些数据的代码如下:

Code:

#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));
        }
    }
}


注意宽度和高度的偏移.因为它们位于一个half-word boundary,以上面的代码访问它们的值将是未对齐内存访问.一些推荐的方式解决这个问题如下:

推荐方案

使用memcpy

我们第一个选择是简单的使用memcpy将数据从buffer处理到我们的局部变量中:

Code:

if (result == fileInfo.dwSize)
{
   MEMCPY(&imageWidth,
          (((byte*)fileBuf)+WIDTH_OFFSET),
          sizeof(uint32));

   MEMCPY(&imageHeight,
          (((byte*)fileBuf)+HEIGHT_OFFSET),
          sizeof(uint32));
}


结果是内存被紧密的拷贝,避免了对齐问题.

使用PACKED编译指令

或者,我们可以使用PACKED编译指令以允许使用指针直接的访问我们想要的数据.也就是强制编译器处理对齐问题.在BREW环境下,PACKED定义如下:

Code:

#ifdef __ARMCC_VERSION
    #define PACKED __packed
#else
    #define PACKED
#endif

通过标明一个指针是PACKED的,ARM编译器将始终生成合适的指令可以正确的访问内存.不管对齐,上边的例子的一个修改的版本,使用PACKED的指针,如下:

Code:

#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;
        }
    }
}

定义Well-Aligned数据结构

虽然我们一般不能控制定制的数据格式,比如上面例子中的BMP文件头,但是,当我们定义自己的数据结构我们可以将数据设计成Well-Aligned方式.以下例子演示这种方式:

Code:

#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没有定义为PACKED,编译器一般将会插入 padding以使每个成员是Well-Aligned的.然而,这通常是不可取的,因为它浪费内存,而且几乎总是可以通过按顺序声明减少大小而避免。

  评论这张
 
阅读(563)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018