钢筋套筒保护帽:从80X86结构看内存对齐问题

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 07:52:10

从80X86结构看内存对齐问题

     最近在学习《The art of Assembly language》,该书对80X86 CPU的内存子系统结构,进行了一个比较详细的介绍,并使我对内存对齐的理解更加深入。

内存访问机制

     内存的写入操作:CPU把需要写入的地址放入地址总线,把需要写入的数据放到数据总线,把控制总线置为写入操作。内存子系统根据地址总线选定内存单元,检查控制总线发现是写入操作,则读取数据总线数据,写入相应的内存单元。以内存地址0:125赋值为0为例说明该问题。如下图所示:

图1 内存写入操作

     内存的读入操作:CPU把需要读入的地址放入地址总线,把控制总线置为读入操作。内存子系统根据地址总线选定内存单元,检查控制总线发现是读入操作,则读取内存单元中的数据,写入数据总线。CPU把数据总线中的数据读到寄存器中。以从内存地址0:125读取数据为例说明该问题。如下图所示:

图2 内存读取操作

内存存放顺序

     80×86中,字节(byte)没有什么问题,每个地址对应一个字节,没有存放顺序的问题。对于字(Word)和双字(double word),需要用2个或者4个字节来存储,所以将会有存储顺序的问题,80×86把低字节,放到最低的地址,高字节存放到其后的字节中;最小的字节地址就是该字(word)或者双字(double word)的地址。

图3 内存顺序

     字节(byte),字(word),双字(double word)可以开始于任何地址,当然通过本文后面的介绍你可以知道,这样做效率上面不一定是最好的。

     另外字节,字,双字可以重叠,比如我们可以说有一个字在193处(低字节193,高字节194),我们也可以说有一个字节在194处,我们还可以说有一个双字在192处(第一个字节192,第二字节193,第三个字节194,第四个字节195)。

8bit数据总线CPU内存对齐问题

     由于8088和80188的数据总线宽度为8bit,一个内存周期只能读取一个字节。每次读取内存都需要给地址总线提供需要读写内存单元的地址。比如用上图的例子比如读取188处的字(word),需要用两次读取,第一次读取188内存单元,第二次读取189内存单元。所以在这种情况下没有内存对齐的问题。

图4 8bit数据总线内存访问

16bit数据总线CPU内存对齐问题

     由于8086,80186,80286和80386sx都是16bit数据总线的CPU,这样一个内存周期可以读取16bit数据也就是一个字(word),两个字节的数据。在这种CPU中,内存单元被分成了偶单元(even)和奇单元(odd),如下图所示:

图5 奇单元和偶单元

     16bit数据总线中,0-7bit与偶单元相连,8-15bit与其单元相连。如下图所示:

图6 16bit数据总线内存访问

     每个内存周期,CPU只能读取一个偶单元和一个奇单元。地址总线的地址为偶单元的地址,所以说地址总线的地址永远是2对齐的。

     假如我们读取一个字(Word)数据,该数据是以2对齐的,则一个内存周期即可把它读出开。如果我们读取字(word)数据不是以2对齐的,那么需要读取两个字节周期来完成。比如读取193内存单元中的字,第一个周期中,CPU把地址192放入地址总线,读取其高字节——地址193(16bit数据总线CPU有字节模式)放入地址总线的8-15bit,第二个周期中,CPU把地址194放入地址总线,内存子系统把读取其低字节——地址194,并把其放入地址总线的0-7bit。注意这是word的顺序是错误的,CPU会自动的交换0-7bit到8-15bit。这样完成一个未对齐字的读写。

     对于读取一个字节,如果该字节的地址是2对齐的,那么CPU直接把该地址放入地址总线,读取该地址的数据,放入数据总线的0-7bit。如果该字节的地址不是以2对齐的,那么CPU把该地址-1的地址放入总线,并读取高字节,放入数据总线的8-15bit,最后CPU自动把8-15bit的数据,交换到0-7bit完成字节的读取。

     对于双字(Double Word)的访问,如果以2对齐,则只需要2个周期即可完成。如果没有以2对齐,则需要3个内存周期来完成,具体原因可以参考字和字节的分析。

32bit数据总线的内存访问

    80386以后的CPU都是32bit的数据总线,数据总线和内存的连接如下图所示:

图7 32bit数据总线内存访问

     通过上图和参考上面16bit数据总线CPU的分析,可以看出32bit数据总线在每个内存周期中读取的数据地址都是以4对齐的。

    如果读取一个双字的地址是以4内存对齐的话,一个内存周期即可读出。如果不是以4对齐,则需要两个内存周期来完成操作。

    如果读取一个字的地址对4取模余3的话,那么读取该字需要两个内存周期(CPU会自动完成高位和低位的交换)。如果读取一个字的地址对4取模不余3的话,则只需要一个内存周期即可完成读写,请自行分析。

     对于字节,任何字节地址的访问都是一个内存周期。

总结

     通过以上分析,我们可以发现CPU的架构决定可内存数据对齐,可以提高内存访问的效率(尽可能少的内存周期来访问数据)。

     很多编译器都有优化代码的功能,优化代码的时候会尽可能的使内存对齐。这也是为什么C语言的结构中经常会出现一些很妖娆的sizeof的问题。

     以上是个人观点,欢迎拍砖。