该文档54行开始写道:Depending on the version of the C compiler you use,
different kernel data structures will contain different alignment of
structures, and...(以下和讨论内容无关,故省略)。如果我没理解错的话,文档中的意思是说不同版本的编译器产生的结构体会有不同的对齐。
但是据我所知,对于给定的平台,对齐应该是确定的吧(不考虑使用__attribute__((packed))等特殊情况),难道不同版本的编译器还能产生不同的对齐代码?如果真有的话,请给个相应的例子。
而且从另一个角度想,如果编译器的版本会影响结构体对齐的话,那么考虑stat这个系统调用:struct
stat是在内核态和用户态之间直接拷贝的,而用户程序编译器和内核编译器的版本没必要也很可能不同。那么,如果不同编译器版本产生的结构器对齐不同的话,stat的正确性如何保证?
谢谢
_______________________________________________
Linux 内核开发中文邮件列表
Linux-...@zh-kernel.org
http://zh-kernel.org/mailman/listinfo/linux-kernel
Linux 内核开发中文社区: http://zh-kernel.org
--
这是zh-kernel邮件列表内容的只读归档,如果想参与讨论请访问http://zh-kernel.org进行订阅。
2010/5/14 YC Wang <wangy...@gmail.com>
感谢回复。我知道理论上X86不对齐也可以访问,但实际上gcc生成的结构体都是按一定规则对齐的吧,除非显式加上__attribute__((packed))。
实际上,我问题的重点是一个结构体是否会因为gcc的版本不同而产生不同的对齐,如果是的话,我上面提到的stat 系统调用的问题如何解释。
在 2010年5月14日 下午1:57,YC Wang <wangy...@gmail.com>写道:
> 在 2010年5月14日 上午11:53,Adam Lee <adam...@gmail.com> 写道:
> > 事实上, 各个平台关于对齐的要求不一样, 有的平台不对齐就无法访问, 会出异常. 但是X86仍然可以访问, 只不过效率有损失. 所以在这里,
> > 不一定所有的编译器都会自动给你添加padding..
> >
>
>
> 感谢回复。我知道理论上X86不对齐也可以访问,但实际上gcc生成的结构体都是按一定规则对齐的吧,除非显式加上__attribute__((packed))。
>
> 实际上,我问题的重点是一个结构体是否会因为gcc的版本不同而产生不同的对齐,如果是的话,我上面提到的stat 系统调用的问题如何解释。
> _______________________________________________
> Linux 内核开发中文邮件列表
> Linux-...@zh-kernel.org
> http://zh-kernel.org/mailman/listinfo/linux-kernel
> Linux 内核开发中文社区: http://zh-kernel.org
>
--
Thanks & Best Regards
Liu Hui
--
刚翻了下K&R的目录, 里面明确说了这里会带来可移植性的问题, 基本上字段描述了位层次上的存储布局就必然带来移植性问题, 具体可以看下
stddef.h中的offsetof(s,m)这个宏.
我比较新手, 自己也不大明白, 希望能帮到. 我的理解是这方面有共同的填充策略
2010/5/14 YC Wang <wangy...@gmail.com>
感谢回复。
可能我还是没有强调出我的问题:我清楚对齐的基础知识,也知道不同的arch有不同的对齐策略,但这不是我要问的。我想问的是在给定的arch下(比如i386),是否会因为编译器_版本_的不同而产生不同的对齐,就像我提到的那篇文档中所说的:Depending
on the version of the C compiler you use, different kernel data
structures will contain different alignment of
structures。如果是的话,那我上面提到的Stat问题如何解释。
谢谢
>
> 刚翻了下K&R的目录, 里面明确说了这里会带来可移植性的问题, 基本上字段描述了位层次上的存储布局就必然带来移植性问题,
> 具体可以看下stddef.h中的offsetof(s,m)这个宏.
>
确实对齐会带来移植的问题,但我这里没有涉及到移植阿:),是在同一个平台上讨论的。
>
> 我比较新手, 自己也不大明白, 希望能帮到. 我的理解是这方面有共同的填充策略
>
总之感谢回复,大家一起进步
......有点较真了,第一个问题的答案时是。编译器版本*可能*会造成不同的对齐。
从第一个问题的结论不能直接推导出第二个问题的结论,因为上面说的仅仅是*可能*。
关于编译器的对齐处理,看看gcc的info和manpage吧。
我觉得没有必要在这个问题上如此较真。stable_api_nosence仅仅是列举出了
可能性并没有说,这些因素在每个版本中都会发生。
这是逻辑问题么?
/大头阿当
stable_api_nonsense.txt 主要就kernel api来分析为什么一个stable kernel api
是没有意义的. kernel internal api相对来说, 定义比较宽松, 经常有变化; kernel
和application之间的api则是非常严格的, 应该现有很多应用还可以运行在10年前
的linux kernel, 整个system call 接口一直都保持兼容.
> structures。如果是的话,那我上面提到的Stat问题如何解释。
你提及的struct stat是kernel和application之间的接口, 这个接口肯定需要考虑
这些问题. 我猜, 对齐的问题对gcc而言, 肯定有个最基本的兼容方法,即按照该方法,
对齐是一定可以保证的, 类似的问题在应用层更多(比如一个library和之上的application, 很难要求用同一版本gcc来编译,
甚至许多lib都是binary发布的)
--
Lei Ming
感谢回复。
首先,这个问题可能比较细节,但应该算不上较真吧,讨论技术问题不就应该得到明确的答案么。即使是“xxx标准规定,xxx会导致未定义行为”,“未定义行为”也是一个明确的答案。
好的,您提到了编译器版本*可能*会造成不同的对齐,那么是不是也可以推断:程序中如果调用了stat系统调用,传给内核的参数struct
stat结构体也*可能*随着编译器的版本有着不同的对齐,而这个结构体在内核和用户程序之间直接拷贝的(详见相应内核代码),如果结构体的对齐都不确定的话,如何保证内核和用户程序对该结构体解释的一致性。
我暂时不会去看gcc的info,因为gcc怎么对齐并不是我关注的内容。我关注的是如果gcc的不同版本会产生不同的对齐,那么对于stat这种情况,用户程序和内核如何“同步”。
谢谢
感谢回复。
你的这种说法倒是一个很好的推理。要是能证明就最好了。比如你说的"对齐的问题对gcc而言,肯定有个最基本的兼容方法",如果能有确切的文档描述就好了。同时,如果能给出一个例子,比如内核中的某个结构体,不符合这个"最基本的兼容方法",那我的疑问就解决了。可能我的要求有些高吧,呵呵。
2010/5/14 YC Wang <wangy...@gmail.com>
OK, Relax. 首先,一个C程序员必须了解的基本常识是,使用结构体作为参数传递的方法是不提倡的。
你恰好给了这样一个例子。它说明这样的参数传递可能存在问题。所以,这仅仅是个因果关系的转换。
仍然是逻辑问题。
stat之所以可以这么做原因在它自身定义的特殊性上,你看看代码
6#ifdef __i386__
7struct stat {
8 unsigned long st_dev;
9 unsigned long st_ino;
10 unsigned short st_mode;
11 unsigned short st_nlink;
12 unsigned short st_uid;
13 unsigned short st_gid;
14 unsigned long st_rdev;
15 unsigned long st_size;
16 unsigned long st_blksize;
17 unsigned long st_blocks;
18 unsigned long st_atime;
19 unsigned long st_atime_nsec;
20 unsigned long st_mtime;
21 unsigned long st_mtime_nsec;
22 unsigned long st_ctime;
23 unsigned long st_ctime_nsec;
24 unsigned long __unused4;
25 unsigned long __unused5;
26};
这种情况下,编译器又多大的可能性可以对它进行不同的padding和对齐?对齐在什么情况下发生?
又在什么情况下必须考虑进去呢?数据的宽度在这里摆着,不需要再多说。
/大头阿当
Each non-bit-field member of a structure or union object is aligned in an implementation-
defined manner appropriate to its type.
看起来,如何对齐取决于编译器的实现,这方面并没有统一的标准。从这方面上讲,是允许不同版本的编译器采用不同的对齐策略的,我理解是这样?有谁研究过GCC这方面的源码?
Yu
YC Wang 写道:
Intel CC呢?
Li Yu 写道:
解决方法是用一些手段去尽量阻止编译器产生不同的struct。
虽然说对齐并不好把握,但是有些规律还是很明显的:
1) 结构体字段顺序不会变,声明的顺序。这也就是说编译器
不可能因为对齐而移动里面的成员顺序。
2) 结构体最开始不可能会被填充,填充只会发生在结构体中间
或者结尾。
(以上两条参考C99 第6.7.2.1)
3) 相邻的unsigned long/long是天然对齐的。
4) 对齐和填充属于编译器的ABI,如果不同版本的某个编译器
破坏了这个ABI,那可以说是这个编译器的bug。
所以正如Adam Jiang所说,内核里定义的struct stat 把
对齐已经做得比较明显了。见arch/x86/include/asm/stat.h。
但是,struct stat 定义在不同平台上是由差异的,所以这个
ABI其实是有变化的,这个变化是由glibc来纠正的,你可以看
glibc的源代码:sysdeps/unix/sysv/linux/xstatconv.c,
以及你的系统里的头文件:/usr/include/bits/stat.h。
On Fri, May 14, 2010 at 05:45:36PM +0800, Li Yu wrote:
>
>我查了查,C99 (WG14/N1124 Committee Draft — May 6, 2005 ISO/IEC 9899:TC2) 6.7.2.1有这么句话:
>
>Each non-bit-field member of a structure or union object is aligned in an implementation-
>defined manner appropriate to its type.
>
>看起来,如何对齐取决于编译器的实现,这方面并没有统一的标准。从这方面上讲,是允许不同版本的编译器采用不同的对齐策略的,我理解是这样?有谁研究过GCC这方面的源码?
这属于ABI,不在C语言标准的范畴中。
gcc自己应该遵循自己的ABI,也就是说,不同版本的gcc应该使用相同的ABI。
请允许我补充一下,gcc的-Wabi选项可以帮助你在编译的时候发现abi不兼容的问题,
在gcc的manpage中,-Wabi节(主要针对g++)也详细说明了因为对齐和其他细节引起的不兼容问题,
也可以看看这里,算是对一般问题的一般说明。
这个是SystemV的ABI,还是GCC自己的ABI?
SystemV AMD64的ABI手册里似乎没有记载。
这个问题问得好。
我查了一下System V ABI IA32,第3章 "Aggregates and Unions" 一节中
有详细讲到:
- An entire structure or union object is aligned on the same boundary as
its most strictly aligned member.
- Each member is assigned to the lowest available offset with the
appropriate alignment. This may require internal padding, depending on the
previous member.
- A structure's size is increased, if necessary, to make it a multiple
of the alignment. This may require tail padding, depending on the last
member.
没错,stat这个结构体确实比较特殊,似乎已经"自然对齐"了,但正是这点阻碍了我们看到问题的本质原因,也就是说stat可以无视编译器的版本正常工作是因为:
1. 不同版本的编译器在给定的平台上可能有不同的结构体对齐策略,但这个stat结构体是一个特例,由于已经"自然对齐",不会受到编译器版本的影响。
或者是因为:
2. 不同版本的编译器在给定的平台上应该有一致的结构体对齐策略,这是由平台ABI规定的(例如SYSV i386
ABI),因此stat结构体的一致性不会受到编译器版本的影响。
你说了"stat之所以可以这么做原因在它自身定义的特殊性上",应该是倾向第一个观点。但是你又能找出一个"不特殊"的结构体,会受到编译器版本的影响么?如果有的话,请给出这么一个结构体以及两个编译器版本。
另外,你说的"一个C程序员必须了解的基本常识是,使用结构体作为参数传递的方法是不提倡的",这个我是不同意的。把结构体作为参数传递是《K&R
C》中描述的,并且没有警告说"不提倡"。采用这种方发的不仅仅是stat系统调用,还有大量的C运行库函数和无数的第三方库函数,难道设计者会反复运用"不提倡"的方法么?同时这也可以反证你之前的观点:库函数的提供者和消费者使用的编译器版本很可能不同,难道涉及的结构体都要经过精心设计而保证"自然对齐"么?
希望你再仔细思考一下,可以看看这个文档
http://www.sco.com/developers/devspecs/abi386-4.pdf 的"Aggregates and
Unions" 一节,其实下面Américo Wang已经贴出来了
我在发起这个讨论的时候还没有看到这个文档,因此那时只是推测应该会在某处规定结构体的对齐问题,否则无法保证使用结构体指针函数的正确性。
之后随着讨论的进行我也找到了这个文档看了上面的描述,那么是不是由此可以得到结论:在给定平台上,结构体的对齐由平台ABI规范的,所有的编译器都应遵照这个规范。也就是说不会因为编译器版本的不同导致结构体对齐的不同,除非是编译器的bug。
如果是这样的话,是不是可以说stable_api_nonsense中"Depending on the version of the C
compiler you use, different kernel data structures will contain
different alignment of structures..."的这个论断并不是很准确,这也是我最初的发起讨论目的。
谢谢
stat这种重要的结构体可能是故意对齐, 先从自身避免了移植性的问题.
另外, 使用结构体而不是用结构体的指针作为参数传递的话, 好像是得把结构体全压到堆栈里, 然后返回个地址, 要是结构体很大的话, 不大好吧.
总之, 这个对齐的问题应该存在, 而且还涉及堆栈空间, 我是觉得不要直接用整个结构体做为参数传递比较好.
2010/5/15 YC Wang <wangy...@gmail.com>:
>
> stat这种重要的结构体可能是故意对齐, 先从自身避免了移植性的问题.
>
我觉得你对移植的理解还不够准确,移植是指从一个平台到另一个平台(例如x86->arm, linux->solaris,
linux/i386->linux/x86_64等等),而我一直在讨论一个给定平台上的问题,假设就是linux/i386,因此这里不存在移植问题
>
> 另外, 使用结构体而不是用结构体的指针作为参数传递的话, 好像是得把结构体全压到堆栈里, 然后返回个地址, 要是结构体很大的话, 不大好吧.
> 总之, 这个对齐的问题应该存在, 而且还涉及堆栈空间, 我是觉得不要直接用整个结构体做为参数传递比较好.
>
没错,这也是使用结构体指针的意义,但和上面一样,这还是我们的讨论无关,因为我也没提过说“结构体不如结构体指针”之类的啊。呵呵
其实讨论到这里已经是两个层面的问题了。
一个问题是对于给定平台来说,结构体的对齐是不是确定的(由平台ABI规定),这也是我发起讨论的目的。
另一个问题实际上是一个posix的结构体在不同的平台间移植的问题,也就是你后边提到的关于glibc的问题。
可能是stat这个结构体太特殊了,同时涉及到上面两个问题,因此把许多人都带到了第二个,恰好偏离了我讨论的初衷,我应该表示歉意。
其实第二个问题本身就够复杂的了,不同操作系统的stat有不同的内部layout,甚至linux本身也有stat和stat64来兼容不同的位数。而solaris中除了stat,stat64外,甚至还有一个stat64_32来专门处理32位应用程序对64位文件的操作,呵呵
可能我没说清, 我后面的意思是在回答你最初的问题, 结构体的存储布局在一个平台上的不用编译环境下, 应该也存在对齐不一致的问题, 所以不建议, 呵呵.
呵呵, 我怎么感觉你的问题答案都出来了啊? 会对齐不一致, 会出错, 但是有标准规范. stat这样的有自身和标准双重保险.
2010/5/15 YC Wang <wangy...@gmail.com>:
首先,存储布局虽然底层,但并不一定和移植相关,而是属于ABI的范畴。除了存储布局,ABI还规定函数调用约定,寄存器使用约定等等。哪怕全世界只有一款处理器一个操作系统,这时虽然不存在移植问题,但ABI依旧发挥作用。
另外,我之前也说了,我知道x86可以不对齐。但我讨论的不是“能不能实现一个生成不对齐的结构体的编译器”,而是“符合标准的编译器,例如gcc,会不会因为版本不同而生成不同对齐的结构体“。希望你能理解我的意思。如果有,希望你能给出一个例子,也就是说一个被不同版本gcc生成了不同对齐的结构体,和相应的gcc版本。
不过总之感谢你的回复,大家一起进步。
ABI上的介绍并没有详细到不同版本的编译器能保持兼容的程度啊,比如最后一段中的
“to make it a multiple of the alignment.”,
没有规定一定是1倍哦。
AMD64的ABI规范中也有类似陈述:3.1.2 中的“Aggregates and Unions”
Okay, okay, Do it as you wish.
/Adam
--
Adam Jiang
-----------------------------------
e-mail:jiang...@gmail.com
http://www.adamjiang.com
-----------------------------------
很难说,因为:
1) 上面的标准并非像C99,C++98那样有权威,即使我们也有LSB指定的标准。
所以有编译器不支持也不奇怪。
2) 理论上同一个编译器的不同版本应该保持相同的ABI,但是,实际上可能是
另一回事。理论和实际分家,在计算机领域还算少吗?:-D
3) 我不是编译器开发者,很可能有一些其它原因我没有考虑到,所以上面的
ABI的问题你最好请教编译器开发者。
--
Live like a child, think like the god.
恩,我给gcc mailing list发邮件问了这个问题,从回复来看,无论什么版本的gcc都始终遵照ABI,除非是有bug或是加了特殊的属性或选项。
不过gcc mailing list的人也不能确定Greg的那句话到底想表达什么意义,建议我写邮件问他。实际上我已经给Greg写过了,只不过他的email-bot建议我去问邮件列表,呵呵
BTW,感谢所有参与这次讨论的人,问题基本有结论了,希望关注的人能够了解。
Hi,
这封邮件回复于好几个月以后,有点奇怪。但是,翻看以前讨论的问题是件挺有意
思的事情,发现自己对一些问题的认识是有问题的。你提了一个很有趣的问题,但
是在五月份的时候,我没有意识到这一点。
GCC版本更新可能会导致ABI的变更,而对齐是ABI的一个重要内容。这里的ABI事实
上是一个操作系统为了支持程序运行必须事先预定好的诸如函数调用方法,参数传
递方法等等真对二进制可执行文件或者可重定位文件的规范。GCC编译的目标就是
这些可执行文件,所以它会尽可能实现某个平台的ABI 。ABI的内容体现在gcc的代
码上就是于平台相关的一些内容,比如
gcc/config/i386/i386.c
gcc/config/mips/mips.c
Linux内核同时也需要实现这些ABI。比如
arch/arm/include/asm/elf.h
arch/arm/kernel/process.c
这些文件。那么,这就说明,ABI是编译器和内核之间约定好的。进而,可以说明
数据结构的对齐也是这些约定的一个部分。
第二个问题是,某个平台的ABI是稳定的,还是会变更的?答案是后者。比如,可
以看看这个网页,它说明的是MIPS的ABI变更历史,
http://www.linux-mips.org/wiki/MIPSABIHistory
现在Linux在MIPS结构上采用o32和n64类似的ABI,并且ABI在不断的变化中,比如
你可以找到一个2009年针对ABI的变更
http://gcc.gnu.org/ml/gcc-patches/2009-04/msg02164.html
这个patch是将MIPS的栈管理方式改变,使它能够支持stack canary抵抗栈溢出攻
击。相应的,内核代码也需要做出变更才能支持GCC的这个变化。这就就是为什么
旧版本的GCC不能编译新内核代码的一个重要原因。虽然我还没有找到直接的对应
于数据结构对齐变更的代码,但是这个例子从一个侧面说明这个变化是切实存在的
。
最后,这种变化是不可预知不可控制的,因为用户的需求在不断的变化。这也是为
什么你看到的文档中反复提到的一个观点。
P.S. Linux内核支持的ABI衍生于SysV ABI,这里有一些比较有用的文档可以参考
,但必须注意的是,真实使用的ABI可能于文档记述的有出入,看代码才能知道真
实的状况。文档提供了整个背景知识。
http://www.sco.com/developers/devspecs/
/大头阿当
> 而且从另一个角度想,如果编译器的版本会影响结构体对齐的话,那么考虑stat
> 这个系统调用:struct stat是在内核态和用户态之间直接拷贝的,而用户程序
> 编译器和内核编译器的版本没必要也很可能不同。那么,如果不同编译器版本产
> 生的结构器对齐不同的话,stat的正确性如何保证?
>
> 谢谢