Chinaunix

标题: 关于字符对齐一问,牛人看过来 [打印本页]

作者: cellar    时间: 2006-11-30 12:33
标题: 关于字符对齐一问,牛人看过来

  1. struct iphdr
  2.   {
  3. #if __BYTE_ORDER == __LITTLE_ENDIAN
  4.     unsigned int ihl:4;
  5.     unsigned int version:4;
  6. #elif __BYTE_ORDER == __BIG_ENDIAN
  7.     unsigned int version:4;
  8.     unsigned int ihl:4;
  9. #else
  10. # error        "Please fix <bits/endian.h>"
  11. #endif
  12.     u_int8_t tos;
  13.     u_int16_t tot_len;
  14.     u_int16_t id;
  15.     u_int16_t frag_off;
  16.     u_int8_t ttl;
  17.     u_int8_t protocol;
  18.     u_int16_t check;
  19.     u_int32_t saddr;
  20.     u_int32_t daddr;
  21.     /*The options start here. */
  22.   };

复制代码

以上是linux中ip.h中ip头的定义,我查了一下arm架构下也是这个头定义
资料中说arm是不是能讯问奇地址的,而且结构是以4字节对齐的
我的问题是,在网络上这么多架构的机器中,为什么可以不加pack类的对齐指令,却能保证传输的正确性呢?

希望我说清楚了问题。
作者: mingyanguo    时间: 2006-11-30 12:58
原帖由 cellar 于 2006-11-30 12:33 发表

struct iphdr
  {
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int ihl:4;
    unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
    unsigned int version:4;
    unsigned i ...

arm是不能访问奇地址的16bit数吧,奇地址的byte应该是没什么问题吧。
作者: mjdcl    时间: 2006-11-30 13:00
IP网络中传输的所有报文都是大端对齐的,所以要存在host和network的字节序转换.
作者: cellar    时间: 2006-11-30 13:30
原帖由 mjdcl 于 2006-11-30 13:00 发表
IP网络中传输的所有报文都是大端对齐的,所以要存在host和network的字节序转换.


你明白我说的是什么吗?kid?
作者: cellar    时间: 2006-11-30 13:35
原帖由 mingyanguo 于 2006-11-30 12:58 发表

arm是不能访问奇地址的16bit数吧,奇地址的byte应该是没什么问题吧。


请注意这里

  1. //前面是双字节对齐的
  2. u_int8_t tos;
  3. u_int16_t tot_len;
复制代码

我认为这里u_int8_t tos会被扩展成双字节存储。但实际测试的情况是并没有这样,而是仍然连续存储,那么下面的u_int16_t tot_len不就是讯问奇地址的双字节数了吗?

因为手头资料也不是很全,解释不了,怕有什么疏漏,以后出现问题。
作者: hellioncu    时间: 2006-11-30 13:47
原帖由 cellar 于 2006-11-30 13:35 发表


请注意这里

  1. //前面是双字节对齐的
  2. u_int8_t tos;
  3. u_int16_t tot_len;
复制代码

我认为这里u_int8_t tos会被扩展成双字节存储。但实际测试的情况是并没有这样,而是仍然连续存储,那么下面的u_int ...



u_int8_t tos前面只有1个字节呀,tot_len还是在偶数地址
作者: yg    时间: 2006-11-30 13:48
32位系统,缺省情况下都是4字节对齐的。
变量是不会扩展的,但下一个非char型变量会从余4为0的地址开始。
作者: cellar    时间: 2006-11-30 13:48
原帖由 hellioncu 于 2006-11-30 13:47 发表



u_int8_t tos前面只有1个字节呀,tot_len还是在偶数地址


眼花花...

作者: hellioncu    时间: 2006-11-30 13:53
原帖由 cellar 于 2006-11-30 13:48 发表


眼花花...


说我还是说你自己呀?
作者: cellar    时间: 2006-11-30 13:57
原帖由 hellioncu 于 2006-11-30 13:53 发表


说我还是说你自己呀?



作者: gvim    时间: 2006-11-30 14:03
可以阿, 难道你的机器上 uchar[10]的数组 for 一遍会出错吗?
早期的arm ldr和str 只能够直接访问 8bit和32bit ,后来的比较新的arm加入了16bit的访问方式。
早期arm需要处理16bit时,是由编译器帮你做位变换,因此虽然功能正确但是效率十分低下。现在的,比如我手上的arm9,对8/16/32的访问都是很直接的。不过寄存器是32位,8/16最后编译器都必须帮你扩展成32,才能放入寄存器,这个问题仍然存在。
作者: cellar    时间: 2006-11-30 14:07
原帖由 yg 于 2006-11-30 13:48 发表
32位系统,缺省情况下都是4字节对齐的。
变量是不会扩展的,但下一个非char型变量会从余4为0的地址开始。


以下为arm9下实测:

  1. //...
  2. #pragma pack (1)
  3. struct xxx{
  4. char a;
  5. long b;
  6. short c;
  7. };
  8. int main(){
  9. struct xxx x;
  10. printf("&a=%p\n&b=%p\n",&x.a,&x.b);
  11. }
复制代码


定义后打印出的结果
&a=0xbffffe8c
&b=0xbffffe8d
作者: cellar    时间: 2006-11-30 14:11
原帖由 gvim 于 2006-11-30 14:03 发表
可以阿, 难道你的机器上 uchar[10]的数组 for 一遍会出错吗?
早期的arm ldr和str 只能够直接访问 8bit和32bit ,后来的比较新的arm加入了16bit的访问方式。
早期arm需要处理16bit时,是由编译器帮你做位变换, ...


这位老大出现了,结论是让人信服的。

以上的程序中有:

  1. struct xxx{
  2. char a;
  3. long b;
  4. short c;
  5. };
复制代码

x86下sizeof(struct xxx); =>7,arm9下sizeof(struct xxx) =>8
是因为arm下的struct空间必须是4的倍数吗?还是因为其它?谢谢


btw:能加我msn吗

[ 本帖最后由 cellar 于 2006-11-30 14:12 编辑 ]
作者: yg    时间: 2006-11-30 14:20
#pragma pack (1)

当然不会做4字节对齐了
作者: cellar    时间: 2006-11-30 14:22
原帖由 yg 于 2006-11-30 14:20 发表
#pragma pack (1)

当然不会做4字节对齐了


不加也不是,兄弟,自己试试
作者: cellar    时间: 2006-11-30 14:25
原帖由 mynets 于 2006-11-30 14:16 发表
哎,设计网络协议的牛人们早考虑到这些了,看看这个ip头,都是32位对齐了的,不用pack了。
访问奇地址的问题,是cpu如何从总线上取数据的问题了,跟程序没什么关系,它可能会一次取整个32位,然后把你需要的8位分 ...


嗯,读内核代码就象读好文学,每读一遍就会有新收获,不到一定的程度就领会不了那个程度的东西啊...
作者: yg    时间: 2006-11-30 14:33
你试过吗?
linux:
&a=0xbfeff3f0
&b=0xbfeff3f4
aix:
&a=2ff22950
&b=2ff22954
sco:
&a=7ffffa24
&b=7ffffa28
没看出有什么特别的
作者: cellar    时间: 2006-11-30 14:45
原帖由 yg 于 2006-11-30 14:33 发表
你试过吗?
linux:
&a=0xbfeff3f0
&b=0xbfeff3f4
aix:
&a=2ff22950
&b=2ff22954
sco:
&a=7ffffa24
&b=7ffffa28
没看出有什么特别的




  1. struct xxx{
  2. char a;
  3. long b;
  4. short c;
  5. };
复制代码

是这个结构??

怎么和我的结果差好多....
作者: yg    时间: 2006-11-30 14:49
用你的程序直接粘贴的
cc -oa a.c
不要加特殊的编译参数
作者: gvim    时间: 2006-11-30 15:09
"老大"这个帽子还是不怎么适合我,呵

我个人认为 对齐需要分两种,一种是处理器的对齐,一种是编译器的对齐。
处理器对齐 在arm上意味着byte必须以8bit对齐,int32必须以32bit对齐。而在x86上,由于CISC处理器特性,它支持十分复杂的存储器操作,这一点从支持的多达10多种 寻址方式上就可以体会出来。因此x86上即使int32也可以按照8bit对齐,只不过损失的是访问效率。
编译器对齐 意味着编译器选择自己喜欢的方式来完成多起操作。如果不能处理器对齐,那么编译器会选择自己喜欢的方式处理。

以arm为例:
下面是你的简短代码

  1. #pragma pack(1)
  2. struct xxx {
  3.     char a;
  4.     long b;
  5.     short c;
  6. };

  7. int
  8. main()
  9. {
  10.     struct xxx m;
  11.     m.a = 'a';
  12.     m.b = 12;
  13.     m.c = 23;
  14.     return sizeof(struct xxx);
  15. }
复制代码

首先看默认的平台,编译器如何处理
vi[~]# arm--netbsdelf-gcc -S mmm.c -o mmm.S
    mov r3, #97
    strb    r3, [fp, #-19]
    mov r3, #0
    orr r3, r3, #12
    strb    r3, [fp, #-18]
    mov r3, #0
    strb    r3, [fp, #-17]
    mov r3, #0
    strb    r3, [fp, #-16]
    mov r3, #0
    strb    r3, [fp, #-15]
    mov r3, #0
    orr r3, r3, #23
    strb    r3, [fp, #-14]
    mov r3, #0
    strb    r3, [fp, #-13]
    mov r3, #7
    mov r0, r3

这里十分明显,这里不能够依赖处理器对齐,因此编译器完全把所有元素都按照strb来处理,效率低下不是一般。但是,sizeof的结果,是7。
将编译参数增加为: arm--netbsdelf-gcc -march=armv4 -S mmm.c -o mmm.S
你可以自己看一下,编译器仍然将所有元素按照strb处理。

如果去掉#pragma pack(1)
仍然使用默认选项编译,结果是
    mov r3, #97
    strb    r3, [fp, #-24]
    mov r3, #12
    str r3, [fp, #-20]
    mov r3, #23
    mov r2, #0
    strb    r3, [fp, #-16]
    strb    r2, [fp, #-15]
    mov r3, #12
    mov r0, r3

当然,sizeof是12,但是最后的short不支持直接操作,编译器帮你换成strb操作。

添加-mach=armv4
    mov r3, #97
    strb    r3, [fp, #-24]
    mov r3, #12
    str r3, [fp, #-20]
    mov r3, #23
    strh    r3, [fp, #-16]  @ movhi
    mov r3, #12
    mov r0, r3

呵呵,新平台,当然就有strh了。
作者: gvim    时间: 2006-11-30 15:12
x86 上就相当直观
#pragma pack(1)
    movb    $97, -24(%ebp)
    movl    $12, -23(%ebp)
    movw    $23, -19(%ebp)
    movl    $7, %eax

/* #pragma pack(1) */
    movb    $97, -24(%ebp)
    movl    $12, -20(%ebp)
    movw    $23, -16(%ebp)
    movl    $12, %eax

作者: 飞灰橙    时间: 2006-11-30 15:25
都是bit fields惹的祸啊
    unsigned int ihl:4;
    unsigned int version:4;
合起来只占了8位,所以尽管ihl和version都是int,
实际只占了一个char的空间,
因为只花费了一个char, 接下来空间自然就对齐,并且保证是无空隙的。

楼主可以将 unsigned int version:4 改成 unsigned int version:5,
结果struct就不会pack在一起了。
作者: cellar    时间: 2006-11-30 15:28
原帖由 gvim 于 2006-11-30 15:12 发表
x86 上就相当直观
#pragma pack(1)

/* #pragma pack(1) */


受教了,崇拜ing...
作者: havey    时间: 2006-11-30 16:35
有点意思!
有些系统屈服于硬件;有些系统屈服于软件,但最终都是屈服于硬件的。
如果你只会c,不去搞点汇编和硬件,还真很难找到点自信!
尤其是在一些思维比较细腻的哥们面前。
作者: connet    时间: 2006-11-30 18:11
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int ihl:4;
    unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
    unsigned int version:4;
    unsigned int ihl:4;
#else
# error        "Please fix <bits/endian.h>"
#endif
    u_int8_t tos;
    u_int16_t tot_len;
上面4字节
    u_int16_t id;
    u_int16_t frag_off;
上面4字节
    u_int8_t ttl;
    u_int8_t protocol;
    u_int16_t check;
上面4字节
    u_int32_t saddr;
上面4字节
    u_int32_t daddr;
上面4字节

任何平台上都是对齐的
协议设计者和软件设计者早考虑到了
作者: 高峰    时间: 2006-11-30 20:55
标题怎么是“字符对齐”??




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2