8086架构的CPU的内存访问机制以及内存对齐(memory alignment)

本文深入探讨了8086处理器的内存访问机制及其背后的内存对齐原理,解释了为什么内存对齐对于提高CPU效率至关重要,并详细说明了编译器如何处理内存对齐,包括使用不同属性和伪指令实现紧凑对齐的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GNU C - 关于8086的内存访问机制以及内存对齐(memory alignment)



接着前面的文章,这篇文章就来说说menory alignment -- 内存对齐.

 

一、为什么需要内存对齐?

 

无论做什么事情,我都习惯性的问自己:为什么我要去做这件事情? 是啊,这可能也是个大家都会去想的问题,

因为我们都不能稀里糊涂的或者。那为什么需要内存对齐呢?这要从cpu的内存访问机制说起.

 

为了了解清楚cpu的内存访问机制,昨天整晚都在查找资料,但是还是找不到很好的介绍资料.后来只是找到了相关

的一些介绍的博客。 这些博客中大多都是以介绍内存对齐为主要目的,然后顺带着说一下cpu的内存访问机制,所以

找不到权威的资料,后来听说<<汇编语言编程艺术>>这本书里面有关于x86的系统介绍,就下载了一份PDF,但是

也还是没有找到.

 

所以呢下面的一些关于x86的内存访问方面的只是很多都是来源于一些比较好的博客.在文章的最后我会注明参考的

博客链接,作为扩展阅读.

 

简单介绍x86的内存访问机制:

1.内存的写入操作: cpu把需要写入的地址放入地址总线, 把需要写入的数据放入数据总线, 把控制总线置为写入操作.

         然后内存子系统根据地址总线选定内存单元, 检查控制总线发现是写入操作,则入去数据总线数据, 写入相关内存

         单元.

 

2.内存的读入操作: cpu把需要读入的地址放入地址总线, 把控制总线置为读入操作. 内存子系统根据地址总线选定内存

         单元, 检查控制总线发现是读入操作, 则读取内存单元中的数据, 写入数据总线.

 

 16bit数据总线: 每个内存周期,cpu只能读取一个偶单元和一个奇单元,地址总线的地址是偶单元的地址,所以地址总线的地址永远是2对齐的.

        每个内存周期,可以读取一个字,也就是16bit.

   1.读取一个字,如果是以2对齐的,则只需要一个内存周期即可完成.如果数据不是以2对齐的,则需要2个内存周期.

   2.读取双字: 如果是以2对齐的,则只需要2个内存周期即可完成,如果数据不是以2对齐的,则需要3个内存周期完成.

 

32bit数据总线: 每个内存周期,读取的数据地址都是以4对齐的.一个内存周期可以读取一个双字,也就是32bit.

    1.如果读取一个双字,地址是以4对齐的话,则只需要一个内存周期即可完成.如果不是以4对齐,则需要2个内存周期完成.

    2.如果读取一个字,地址是对4取模余3的话,那么需要2个内存周期完成对数据的读取.地址如果对4去模不余3的话,则

       只需要一个内存周期即可完成数据读取.

    3. 对于字节, 任何字节地址读取只需要一个内存周期.

 

通过上面可以看得出,为什么16bit数据总线cpu是以2对齐的,而32bit数据总线cpu是以4对齐的. 最主要的原因是能够在最小的

内存周期内完成对地址的访问,提高cpu的效率.

 

二、内存对齐的作用

   如果不采用内存对齐机制的话,有些地址的访问需要在多个内存周期内完成,而且还需要多次内存周期读取的高低字节

进行拼凑,然后得到32bit数据.  如果使用内存对齐机制,不仅可以减少对地址访问过程中需要的内存周期,而且还避免了

高低字节的数据拼凑,提高了cpu的工作效率.

 

三、编译器是如何处理内存对齐的?

 

1
2
3
4
5
6
7
8
struct mem_alignment
{
    char     a;
 
    int      b;
 
    char     c;
};

 

 

 在32位x86机器上面它的大小是12. 另外一个问题,如果结构体中的成员变量顺序不一样会导致该结构在内存中的长度

 也不一样,就像上面,如果改成下面这个样子:

 

1
2
3
4
5
6
7
8
struct mem_alignment
{
    char     a;
 
    char     c;
 
    int      b;
};

 

 那么它的大小就变成了8.

 

 如果我们使用紧凑的对齐方式 __attribute__((packed)) or  __attribute__((aligned (1)))的话,

 那么struct mem_alignment的大小应该是6. 或者是使用伪指令#pragma pack (1).

 

1
2
3
4
5
6
7
8
9
10
11
12
#pragma pack (1)
 
struct mem_alignment
{
    char     a;
 
    char     c;
 
    int      b;
};
 
#pragma pack ()

 

 

  上面最后一句的作用是恢复编译器默认的对齐方式.

 
关于内存对齐方面的知识就总结到这里. 也算是对前面文章的交代了~

 

参考资料:

          <<从80X86结构看内存对齐问题>> http://my.unix-center.net/~Simon_fu/?p=262

          <<oschina 内存对齐的问题>>  http://www.oschina.net/question/234345_48055

          <<Thinking in linux C/C++字节对齐详解>> http://www.linuxsong.org/2010/09/c-byte-alignment/

 

<think>好的,我需要处理用户关于MCU非对齐内存访问的问题。首先,我需要回忆一下之前用户提供的引用内容,特别是引用[1]、[2]、[3]、[4]。这些引用提到了非对齐访问的条件、性能影响、解决方案以及调试方法。 用户的问题集中在MCU的非对齐内存访问问题及解决方案。我需要结合这些引用,整理出清晰的结构。首先,应该解释什么是非对齐访问,然后讨论它带来的问题,比如异常触发、性能影响和兼容性问题。接着,要给出解决方案,比如编译器设置、手动对齐内存管理配置和固件库使用。同时,需要提到调试方法,如检查SCTLR寄存器、使用调试工具和代码审查。 需要注意引用中的关键点:STM32MP135的ARMv7架构支持非对齐访问的条件,包括SCTLR寄存器的设置和内存类型。当MMU禁用时,内存被视为Strongly-ordered,可能导致对齐错误。此外,性能优化方面,非对齐访问可能导致多次内存访问,影响效率。解决方案中提到的将变量拷贝到RAM可能适用,如引用[3]中的DSP程序例子。 需要确保回答结构清晰,分点说明问题和解决方案,并适当引用资料。同时,用户要求生成相关问题,可能需要包括异常处理、性能影响、ARM架构差异、调试工具等。最后,检查LaTeX格式是否正确,所有数学表达式用$...$,独立公式用$$...$$,但当前内容中可能较少涉及公式,需注意是否有需要的地方。 现在组织内容:先定义非对齐访问,再问题,再解决方案,最后相关问题。确保引用标注正确,每个引用对应到正确的部分。例如,引用[1]关于触发条件,引用[2]关于性能,引用[3]的解决方案,引用[4]的调试方法。 可能还需要补充一些实际例子或代码片段,但用户提供的引用中没有具体代码,可能需要避免。最后,确认使用中文,语法正确,LaTeX格式无误。</think>### MCU非对齐内存访问问题及解决方案 #### 一、非对齐访问的定义 当数据存储在内存中的地址**不是其数据类型大小的整数倍**时,称为非对齐访问。例如: - 32位(4字节)变量存放在地址$0x00000002$(非4的倍数) - 16位(2字节)变量存放在地址$0x00000003$(非2的倍数) #### 二、非对齐访问引发的问题 1. **硬件异常触发** ARM Cortex-M系列中,若内核未启用非对齐访问支持,或访问的地址空间被定义为**Device/Strongly-ordered内存**(如MMU禁用时),会触发`HardFault`或`BusFault`[^1][^4]。例如STM32MP135的默认MMU禁用状态下,所有内存均视为Strongly-ordered类型,非对齐访问直接报错。 2. **性能损失** 非对齐访问可能导致CPU需要多次内存读取操作。例如32位变量存放在地址$0x00000001$时,需读取地址$0x00000000$和$0x00000004$的两个32位数据块,再拼接有效部分,增加时钟周期[^2]。 3. **跨平台兼容性问题** 某些MCU架构(如MIPS、早期ARMv6)强制要求对齐访问,非对齐代码移植时可能崩溃。 #### 三、解决方案 1. **编译器配置优化** - 使用`__attribute__((aligned(n)))`强制变量对齐(GCC/Clang): ```c uint32_t __attribute__((aligned(4))) buffer[10]; // 强制4字节对齐 ``` - 启用编译器的非对齐访问支持(需硬件支持): 在ARM Cortex-M3/M4中,通过设置`SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk;`允许非对齐访问。 2. **手动数据对齐** 对结构体进行填充或调整成员顺序: ```c typedef struct { uint8_t flag; // 1字节 uint8_t padding[3];// 填充3字节 uint32_t value; // 4字节对齐 } CustomStruct; // 结构体总大小为8字节 ``` 3. **内存管理配置** 启用MMU并将内存区域标记为**Normal Memory**(支持非对齐访问)[^1]。例如STM32MP135中,配置MMU的页表属性为: ```c MMU_SetPageTable(addr, MT_NORMAL); // 设置内存类型为Normal ``` 4. **固件库辅助处理** 使用库函数替代直接内存操作,如`memcpy`复制非对齐数据: ```c uint32_t read_unaligned(uint8_t *addr) { uint32_t value; memcpy(&value, addr, 4); // 避免直接*(uint32_t*)addr return value; } ``` #### 四、调试方法 1. **检查SCTLR寄存器** 确认`CP15.SCTLR.A`位(Alignment Check Enable)是否为0,允许非对齐访问[^1]。 2. **使用调试工具捕获异常** 在Keil/IAR中启用`HardFault`中断的断点,分析LR寄存器的返回地址定位问题代码。 3. **静态代码分析** 使用PC-Lint等工具检测潜在的非对齐访问代码。 #### 五、典型场景示例 **案例:DSP程序在Flash中变量地址不对齐** 问题现象:DSP从Flash读取数据时因非对齐访问崩溃。 解决方案:将关键变量拷贝至RAM(RAM通常被配置为Normal Memory)[^3]: ```c #pragma SECTION("RAM_SECTION") // 指定变量分配到RAM uint32_t Ki_Addr[256]; ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值