1*26f1a50fSYanteng Si.. include:: ../disclaimer-zh_CN.rst 2*26f1a50fSYanteng Si 3*26f1a50fSYanteng Si:Original: Documentation/core-api/unaligned-memory-access.rst 4*26f1a50fSYanteng Si 5*26f1a50fSYanteng Si:翻译: 6*26f1a50fSYanteng Si 7*26f1a50fSYanteng Si 司延腾 Yanteng Si <siyanteng@loongson.cn> 8*26f1a50fSYanteng Si 9*26f1a50fSYanteng Si:校译: 10*26f1a50fSYanteng Si 11*26f1a50fSYanteng Si 时奎亮 <alexs@kernel.org> 12*26f1a50fSYanteng Si 13*26f1a50fSYanteng Si.. _cn_core-api_unaligned-memory-access: 14*26f1a50fSYanteng Si 15*26f1a50fSYanteng Si============== 16*26f1a50fSYanteng Si非对齐内存访问 17*26f1a50fSYanteng Si============== 18*26f1a50fSYanteng Si 19*26f1a50fSYanteng Si:作者: Daniel Drake <dsd@gentoo.org>, 20*26f1a50fSYanteng Si:作者: Johannes Berg <johannes@sipsolutions.net> 21*26f1a50fSYanteng Si 22*26f1a50fSYanteng Si:感谢他们的帮助: Alan Cox, Avuton Olrich, Heikki Orsila, Jan Engelhardt, 23*26f1a50fSYanteng Si Kyle McMartin, Kyle Moffett, Randy Dunlap, Robert Hancock, Uli Kunitz, 24*26f1a50fSYanteng Si Vadim Lobanov 25*26f1a50fSYanteng Si 26*26f1a50fSYanteng Si 27*26f1a50fSYanteng SiLinux运行在各种各样的架构上,这些架构在内存访问方面有不同的表现。本文介绍了一些 28*26f1a50fSYanteng Si关于不对齐访问的细节,为什么你需要编写不引起不对齐访问的代码,以及如何编写这样的 29*26f1a50fSYanteng Si代码 30*26f1a50fSYanteng Si 31*26f1a50fSYanteng Si 32*26f1a50fSYanteng Si非对齐访问的定义 33*26f1a50fSYanteng Si================ 34*26f1a50fSYanteng Si 35*26f1a50fSYanteng Si当你试图从一个不被N偶数整除的地址(即addr % N != 0)开始读取N字节的数据时,就 36*26f1a50fSYanteng Si会发生无对齐内存访问。例如,从地址0x10004读取4个字节的数据是可以的,但从地址 37*26f1a50fSYanteng Si0x10005读取4个字节的数据将是一个不对齐的内存访问。 38*26f1a50fSYanteng Si 39*26f1a50fSYanteng Si上述内容可能看起来有点模糊,因为内存访问可以以不同的方式发生。这里的背景是在机器 40*26f1a50fSYanteng Si码层面上:某些指令在内存中读取或写入一些字节(例如x86汇编中的movb、movw、movl)。 41*26f1a50fSYanteng Si正如将变得清晰的那样,相对容易发现那些将编译为多字节内存访问指令的C语句,即在处理 42*26f1a50fSYanteng Siu16、u32和u64等类型时。 43*26f1a50fSYanteng Si 44*26f1a50fSYanteng Si 45*26f1a50fSYanteng Si自然对齐 46*26f1a50fSYanteng Si======== 47*26f1a50fSYanteng Si 48*26f1a50fSYanteng Si上面提到的规则构成了我们所说的自然对齐。当访问N个字节的内存时,基础内存地址必须被 49*26f1a50fSYanteng SiN平均分割,即addr % N == 0。 50*26f1a50fSYanteng Si 51*26f1a50fSYanteng Si在编写代码时,假设目标架构有自然对齐的要求。 52*26f1a50fSYanteng Si 53*26f1a50fSYanteng Si在现实中,只有少数架构在所有大小的内存访问上都要求自然对齐。然而,我们必须考虑所 54*26f1a50fSYanteng Si有支持的架构;编写满足自然对齐要求的代码是实现完全可移植性的最简单方法。 55*26f1a50fSYanteng Si 56*26f1a50fSYanteng Si 57*26f1a50fSYanteng Si为什么非对齐访问时坏事 58*26f1a50fSYanteng Si====================== 59*26f1a50fSYanteng Si 60*26f1a50fSYanteng Si执行非对齐内存访问的效果因架构不同而不同。在这里写一整篇关于这些差异的文档是很容 61*26f1a50fSYanteng Si易的;下面是对常见情况的总结: 62*26f1a50fSYanteng Si 63*26f1a50fSYanteng Si - 一些架构能够透明地执行非对齐内存访问,但通常会有很大的性能代价。 64*26f1a50fSYanteng Si - 当不对齐的访问发生时,一些架构会引发处理器异常。异常处理程序能够纠正不对齐的 65*26f1a50fSYanteng Si 访问,但要付出很大的性能代价。 66*26f1a50fSYanteng Si - 一些架构在发生不对齐访问时,会引发处理器异常,但异常中并没有包含足够的信息来 67*26f1a50fSYanteng Si 纠正不对齐访问。 68*26f1a50fSYanteng Si - 有些架构不能进行无对齐内存访问,但会默默地执行与请求不同的内存访问,从而导致 69*26f1a50fSYanteng Si 难以发现的微妙的代码错误! 70*26f1a50fSYanteng Si 71*26f1a50fSYanteng Si从上文可以看出,如果你的代码导致不对齐的内存访问发生,那么你的代码在某些平台上将无 72*26f1a50fSYanteng Si法正常工作,在其他平台上将导致性能问题。 73*26f1a50fSYanteng Si 74*26f1a50fSYanteng Si不会导致非对齐访问的代码 75*26f1a50fSYanteng Si======================== 76*26f1a50fSYanteng Si 77*26f1a50fSYanteng Si起初,上面的概念似乎有点难以与实际编码实践联系起来。毕竟,你对某些变量的内存地址没 78*26f1a50fSYanteng Si有很大的控制权,等等。 79*26f1a50fSYanteng Si 80*26f1a50fSYanteng Si幸运的是事情并不复杂,因为在大多数情况下,编译器会确保代码工作正常。例如,以下面的 81*26f1a50fSYanteng Si结构体为例:: 82*26f1a50fSYanteng Si 83*26f1a50fSYanteng Si struct foo { 84*26f1a50fSYanteng Si u16 field1; 85*26f1a50fSYanteng Si u32 field2; 86*26f1a50fSYanteng Si u8 field3; 87*26f1a50fSYanteng Si }; 88*26f1a50fSYanteng Si 89*26f1a50fSYanteng Si让我们假设上述结构体的一个实例驻留在从地址0x10000开始的内存中。根据基本的理解,访问 90*26f1a50fSYanteng Sifield2会导致非对齐访问,这并不是不合理的。你会期望field2位于该结构体的2个字节的偏移 91*26f1a50fSYanteng Si量,即地址0x10002,但该地址不能被4平均整除(注意,我们在这里读一个4字节的值)。 92*26f1a50fSYanteng Si 93*26f1a50fSYanteng Si幸运的是,编译器理解对齐约束,所以在上述情况下,它会在field1和field2之间插入2个字节 94*26f1a50fSYanteng Si的填充。因此,对于标准的结构体类型,你总是可以依靠编译器来填充结构体,以便对字段的访 95*26f1a50fSYanteng Si问可以适当地对齐(假设你没有将字段定义不同长度的类型)。 96*26f1a50fSYanteng Si 97*26f1a50fSYanteng Si同样,你也可以依靠编译器根据变量类型的大小,将变量和函数参数对齐到一个自然对齐的方案。 98*26f1a50fSYanteng Si 99*26f1a50fSYanteng Si在这一点上,应该很清楚,访问单个字节(u8或char)永远不会导致无对齐访问,因为所有的内 100*26f1a50fSYanteng Si存地址都可以被1均匀地整除。 101*26f1a50fSYanteng Si 102*26f1a50fSYanteng Si在一个相关的话题上,考虑到上述因素,你可以观察到,你可以对结构体中的字段进行重新排序, 103*26f1a50fSYanteng Si以便将字段放在不重排就会插入填充物的地方,从而减少结构体实例的整体常驻内存大小。上述 104*26f1a50fSYanteng Si例子的最佳布局是:: 105*26f1a50fSYanteng Si 106*26f1a50fSYanteng Si struct foo { 107*26f1a50fSYanteng Si u32 field2; 108*26f1a50fSYanteng Si u16 field1; 109*26f1a50fSYanteng Si u8 field3; 110*26f1a50fSYanteng Si }; 111*26f1a50fSYanteng Si 112*26f1a50fSYanteng Si对于一个自然对齐方案,编译器只需要在结构的末尾添加一个字节的填充。添加这种填充是为了满 113*26f1a50fSYanteng Si足这些结构的数组的对齐约束。 114*26f1a50fSYanteng Si 115*26f1a50fSYanteng Si另一点值得一提的是在结构体类型上使用__attribute__((packed))。这个GCC特有的属性告诉编 116*26f1a50fSYanteng Si译器永远不要在结构体中插入任何填充,当你想用C结构体来表示一些“off the wire”的固定排列 117*26f1a50fSYanteng Si的数据时,这个属性很有用。 118*26f1a50fSYanteng Si 119*26f1a50fSYanteng Si你可能会倾向于认为,在访问不满足架构对齐要求的字段时,使用这个属性很容易导致不对齐的访 120*26f1a50fSYanteng Si问。然而,编译器也意识到了对齐的限制,并且会产生额外的指令来执行内存访问,以避免造成不 121*26f1a50fSYanteng Si对齐的访问。当然,与non-packed的情况相比,额外的指令显然会造成性能上的损失,所以packed 122*26f1a50fSYanteng Si属性应该只在避免结构填充很重要的时候使用。 123*26f1a50fSYanteng Si 124*26f1a50fSYanteng Si 125*26f1a50fSYanteng Si导致非对齐访问的代码 126*26f1a50fSYanteng Si==================== 127*26f1a50fSYanteng Si 128*26f1a50fSYanteng Si考虑到上述情况,让我们来看看一个现实生活中可能导致非对齐内存访问的函数的例子。下面这个 129*26f1a50fSYanteng Si函数取自include/linux/etherdevice.h,是一个优化的例程,用于比较两个以太网MAC地址是否 130*26f1a50fSYanteng Si相等:: 131*26f1a50fSYanteng Si 132*26f1a50fSYanteng Si bool ether_addr_equal(const u8 *addr1, const u8 *addr2) 133*26f1a50fSYanteng Si { 134*26f1a50fSYanteng Si #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 135*26f1a50fSYanteng Si u32 fold = ((*(const u32 *)addr1) ^ (*(const u32 *)addr2)) | 136*26f1a50fSYanteng Si ((*(const u16 *)(addr1 + 4)) ^ (*(const u16 *)(addr2 + 4))); 137*26f1a50fSYanteng Si 138*26f1a50fSYanteng Si return fold == 0; 139*26f1a50fSYanteng Si #else 140*26f1a50fSYanteng Si const u16 *a = (const u16 *)addr1; 141*26f1a50fSYanteng Si const u16 *b = (const u16 *)addr2; 142*26f1a50fSYanteng Si return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) == 0; 143*26f1a50fSYanteng Si #endif 144*26f1a50fSYanteng Si } 145*26f1a50fSYanteng Si 146*26f1a50fSYanteng Si在上述函数中,当硬件具有高效的非对齐访问能力时,这段代码没有问题。但是当硬件不能在任意 147*26f1a50fSYanteng Si边界上访问内存时,对a[0]的引用导致从地址addr1开始的2个字节(16位)被读取。 148*26f1a50fSYanteng Si 149*26f1a50fSYanteng Si想一想,如果addr1是一个奇怪的地址,如0x10003,会发生什么?(提示:这将是一个非对齐访 150*26f1a50fSYanteng Si问。) 151*26f1a50fSYanteng Si 152*26f1a50fSYanteng Si尽管上述函数存在潜在的非对齐访问问题,但它还是被包含在内核中,但被理解为只在16位对齐 153*26f1a50fSYanteng Si的地址上正常工作。调用者应该确保这种对齐方式或者根本不使用这个函数。这个不对齐的函数 154*26f1a50fSYanteng Si仍然是有用的,因为它是在你能确保对齐的情况下的一个很好的优化,这在以太网网络环境中几 155*26f1a50fSYanteng Si乎是一直如此。 156*26f1a50fSYanteng Si 157*26f1a50fSYanteng Si 158*26f1a50fSYanteng Si下面是另一个可能导致非对齐访问的代码的例子:: 159*26f1a50fSYanteng Si 160*26f1a50fSYanteng Si void myfunc(u8 *data, u32 value) 161*26f1a50fSYanteng Si { 162*26f1a50fSYanteng Si [...] 163*26f1a50fSYanteng Si *((u32 *) data) = cpu_to_le32(value); 164*26f1a50fSYanteng Si [...] 165*26f1a50fSYanteng Si } 166*26f1a50fSYanteng Si 167*26f1a50fSYanteng Si每当数据参数指向的地址不被4均匀整除时,这段代码就会导致非对齐访问。 168*26f1a50fSYanteng Si 169*26f1a50fSYanteng Si综上所述,你可能遇到非对齐访问问题的两种主要情况包括: 170*26f1a50fSYanteng Si 171*26f1a50fSYanteng Si 1. 将变量定义不同长度的类型 172*26f1a50fSYanteng Si 2. 指针运算后访问至少2个字节的数据 173*26f1a50fSYanteng Si 174*26f1a50fSYanteng Si 175*26f1a50fSYanteng Si避免非对齐访问 176*26f1a50fSYanteng Si============== 177*26f1a50fSYanteng Si 178*26f1a50fSYanteng Si避免非对齐访问的最简单方法是使用<asm/unaligned.h>头文件提供的get_unaligned()和 179*26f1a50fSYanteng Siput_unaligned()宏。 180*26f1a50fSYanteng Si 181*26f1a50fSYanteng Si回到前面的一个可能导致非对齐访问的代码例子:: 182*26f1a50fSYanteng Si 183*26f1a50fSYanteng Si void myfunc(u8 *data, u32 value) 184*26f1a50fSYanteng Si { 185*26f1a50fSYanteng Si [...] 186*26f1a50fSYanteng Si *((u32 *) data) = cpu_to_le32(value); 187*26f1a50fSYanteng Si [...] 188*26f1a50fSYanteng Si } 189*26f1a50fSYanteng Si 190*26f1a50fSYanteng Si为了避免非对齐的内存访问,你可以将其改写如下:: 191*26f1a50fSYanteng Si 192*26f1a50fSYanteng Si void myfunc(u8 *data, u32 value) 193*26f1a50fSYanteng Si { 194*26f1a50fSYanteng Si [...] 195*26f1a50fSYanteng Si value = cpu_to_le32(value); 196*26f1a50fSYanteng Si put_unaligned(value, (u32 *) data); 197*26f1a50fSYanteng Si [...] 198*26f1a50fSYanteng Si } 199*26f1a50fSYanteng Si 200*26f1a50fSYanteng Siget_unaligned()宏的工作原理与此类似。假设'data'是一个指向内存的指针,并且你希望避免 201*26f1a50fSYanteng Si非对齐访问,其用法如下:: 202*26f1a50fSYanteng Si 203*26f1a50fSYanteng Si u32 value = get_unaligned((u32 *) data); 204*26f1a50fSYanteng Si 205*26f1a50fSYanteng Si这些宏适用于任何长度的内存访问(不仅仅是上面例子中的32位)。请注意,与标准的对齐内存 206*26f1a50fSYanteng Si访问相比,使用这些宏来访问非对齐内存可能会在性能上付出代价。 207*26f1a50fSYanteng Si 208*26f1a50fSYanteng Si如果使用这些宏不方便,另一个选择是使用memcpy(),其中源或目标(或两者)的类型为u8*或 209*26f1a50fSYanteng Si非对齐char*。由于这种操作的字节性质,避免了非对齐访问。 210*26f1a50fSYanteng Si 211*26f1a50fSYanteng Si 212*26f1a50fSYanteng Si对齐 vs. 网络 213*26f1a50fSYanteng Si============= 214*26f1a50fSYanteng Si 215*26f1a50fSYanteng Si在需要对齐负载的架构上,网络要求IP头在四字节边界上对齐,以优化IP栈。对于普通的以太网 216*26f1a50fSYanteng Si硬件,常数NET_IP_ALIGN被使用。在大多数架构上,这个常数的值是2,因为正常的以太网头是 217*26f1a50fSYanteng Si14个字节,所以为了获得适当的对齐,需要DMA到一个可以表示为4*n+2的地址。一个值得注意的 218*26f1a50fSYanteng Si例外是powerpc,它将NET_IP_ALIGN定义为0,因为DMA到未对齐的地址可能非常昂贵,与未对齐 219*26f1a50fSYanteng Si的负载的成本相比相形见绌。 220*26f1a50fSYanteng Si 221*26f1a50fSYanteng Si对于一些不能DMA到未对齐地址的以太网硬件,如4*n+2或非以太网硬件,这可能是一个问题,这 222*26f1a50fSYanteng Si时需要将传入的帧复制到一个对齐的缓冲区。因为这在可以进行非对齐访问的架构上是不必要的, 223*26f1a50fSYanteng Si所以可以使代码依赖于CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS,像这样:: 224*26f1a50fSYanteng Si 225*26f1a50fSYanteng Si #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 226*26f1a50fSYanteng Si skb = original skb 227*26f1a50fSYanteng Si #else 228*26f1a50fSYanteng Si skb = copy skb 229*26f1a50fSYanteng Si #endif 230