免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 12313 | 回复: 2

[内存管理] bitfield与endian [复制链接]

论坛徽章:
13
程序设计版块每日发帖之星
日期:2016-06-29 06:20:00每日论坛发贴之星
日期:2016-08-14 06:20:00操作系统版块每日发帖之星
日期:2016-08-14 06:20:00每日论坛发贴之星
日期:2016-08-13 06:20:00数据库技术版块每日发帖之星
日期:2016-08-13 06:20:00程序设计版块每日发帖之星
日期:2016-08-13 06:20:00IT运维版块每日发帖之星
日期:2016-08-13 06:20:00每日论坛发贴之星
日期:2016-08-12 06:20:00数据库技术版块每日发帖之星
日期:2016-08-12 06:20:00程序设计版块每日发帖之星
日期:2016-08-12 06:20:00操作系统版块每日发帖之星
日期:2016-08-12 06:20:00综合交流区版块每日发帖之星
日期:2016-08-09 06:20:00
发表于 2016-06-26 09:25 |显示全部楼层
本帖最后由 karma303 于 2016-08-28 10:33 编辑

  网上开始关注bitfield的endian特性,似乎都是从linux源代码的一个细节开始的。
  1. struct iphdr {
  2. #if defined(__LITTLE_ENDIAN_BITFIELD)
  3.     __u8    ihl:4,
  4.         version:4;
  5. #elif defined (__BIG_ENDIAN_BITFIELD)
  6.     __u8    version:4,
  7.           ihl:4;
  8. #else
  9. #error    "Please fix <asm/byteorder.h>"
  10.     ....
  11. }
复制代码

  一些朋友看到这段代码,再品味一下"__BIG_ENDIAN_BITFIELD"这个宏,就会有种被颠覆三观的感觉。因为在我们心目中,cpu只有byte order之说,没有bit order之说。
  一个byte内部的bit order也分big endian和little endian吗?
  可以肯定的是,对CPU来说,也就是软件编程中,我们不需要关心bit order的endianness,它是硬件层的事情。
Bit endianness or bit-level endianness refers to the transmission order of bits over a serial medium. The bit-level analogue of little-endian (least significant bit goes first) is used in RS-232, Ethernet, and USB ... Usually the order of bits is transparently managed by the hardware and only relevant on this very low level ...

上面提到了以太网,有些人肯定心想,果然,我确实在linux的网络模块的源码里,看到很多的ntohx和htonx。
  其实那仍然是字节序的转换,跟这里的bit order不相干。这里的Ethernet指的是硬件层的,连驱动都不需要关心bit order。
  并且,真正到了硬件层,到了关心Bit Endianness的地方,大家反而不叫它" Bit Endianness"了,而是直接唤作LSB first和MSB first。
thus terms like LSB first and MSB first are more descriptive than the concept of endianness.

我们用下面这段话再坚定一下我们的三观:
Within a byte, for all processors, bit 7 is the most significant bit. So the big end byte looks the same for both byte orderings. Usually in printed material this bit is shown at the left, as in 00010010.

好了,既然没有所谓的bit order,那怎么还会那样子呢?(你还记得是哪样子吗)
  事情是这样的,大多数人在阅读linux源码的时候,特别是习惯了inte体系结构的读者,脑子中只有一种内存模型。随便说一个数字,0xabcdef00,我们脑海里立刻就浮现出一个画面:(假设这个int位于0x7c00地址处)

上面是经典的x86的内存视图。假如机器换做MIPS呢,我们会不假思索的构画出big endian下的布局:


  一切看上去正常。
  现在,假如在MIPS机器上,有这么一个结构体。
  1. -------------m.c---------------
  2.   struct abcdef {
  3.     char ab;
  4.     int d: 4;
  5.     int c: 4;
  6.     char ef;
  7.     char oo;
  8.   }abcdefoo = { ab: 0xab, d: 0xd, c: 0xc, ef: 0xef, oo: 0};
  9.   -----------------------------------------------------
复制代码
我们会觉得,这只不过是一个写的比较啰嗦的0xabcdef00。出来的值还是一样的。
但我们错了。看一下。
wws@localhost:~/lab/test$ mips-linux-gnu-gcc -c -o m.o m.c
wws@localhost:~/lab/test$ objdump -s m.o|grep data -A1
Contents of section .data:
0000 00000102 abdcef00 00000000 00000000  ...............


  在MIPS眼中,这个结构体的value是0xabdcef00。两个bitfield的位置颠倒了。
  这就是问题所在,gcc开发者眼中的big endian的内存模型,其实是这样:


  其实在大端模式下,本来就该这样看内存,这样非常自然。
  现在问题就明白了,在gcc编译器作者眼中,你指定的结构体的布局是这样来的:
  >>>>>>>>>memory address growth >>>>>>>>>>>>>
  (char ab)  (int d:4)  (int c:4)  (char ef)  (char oo)

  那么,编译器当然会生成 0xabdcef00这个value。

  总结一下,这个问题,不是bit order的问题。是编译器的问题。更确切说,是编译器作者的问题。
  为什么bitfield为什么会受 byte endian的影响呢。因为byte endian一变,编译器作者看待内存的眼光(他心中用的内存模型)也就变了,所以bitfield的生长方向也就变了。

----------------------7月1日更新-----------------------
【当bitfield跨字节。。。】

  下面展示(short *)0x7c00 = 0xABCD 这条代码分别在MIPS和X86机器上执行后,内存中的布局。
up.png


上面两张图,就是gcc编译器作者眼中的内存模型。注意两点:
1,它们的内存地址生长的方向不同。MIPS向右,X86向左。
2,它们的比特位都是(注意,都是)从右往左对应7~0。

我想说的是第二点,比特位的生长顺序非常重要。
就这一点,我们要与gcc编译器的作者达成共识。它们眼中的bit生长顺序就是这样。
然后,我们再看gcc对于大小端上bitfield的排布,就容易理解了。

这样一段代码:
  1. #include<stio.h>
  2. #pragma pack(1)
  3. struct ab{
  4.         union{
  5.                 struct {
  6.                         int m: 13;
  7.                         int n: 3;
  8.                 };
  9.                 struct {
  10.                         unsigned char a;
  11.                         unsigned char b;
  12.                 };
  13.         };
  14. };
  15. void main(void){
  16.         struct ab ab;
  17.         ab.m = (0x34<<5) + 1;
  18.         ab.n = 1;
  19.         printf("a:%x, b:%x\n", ab.a, ab.b);
  20. }
复制代码


在x86和mips上的输出分别是:
wws@localhost:~/lab/test$ gcc -o x86.elf a.c
wws@localhost:~/lab/test$ mips-linux-gnu-gcc -o mips.elf a.c -static
wws@localhost:~/lab/test$ ./x86.elf
a:81, b:26
wws@localhost:~/lab/test$ qemu-mips ./mips.elf
a:34, b:9


对照上面的内存模型(内存地址就不改了),很容易解释:
bottom.png


===2016,7,28更新=======
还是说明一下为好,就像我的别的大部分文章,这一篇照例不是写给新手的。甚至说,你得对字节序,比特序,bitfiled有相当多的思考后,才适宜读它。

参考资料:
chortle.ccsu.edu/AssemblyTutorial/Chapter-15/ass15_4.html
en.wikipedia.org/wiki/Endianness

下一篇:biefield与endian 2



2016-08-28-075142_433x121_scrot.png
2016-08-28-080552_425x123_scrot.png
2016-08-28-080711_428x129_scrot.png
2016-08-28-081001_421x121_scrot.png

论坛徽章:
9
程序设计版块每日发帖之星
日期:2016-02-13 06:20:00数据库技术版块每日发帖之星
日期:2016-06-15 06:20:00数据库技术版块每日发帖之星
日期:2016-06-16 06:20:00数据库技术版块每日发帖之星
日期:2016-06-18 06:20:00程序设计版块每日发帖之星
日期:2016-06-27 06:20:00程序设计版块每日发帖之星
日期:2016-07-09 06:20:00IT运维版块每日发帖之星
日期:2016-07-15 06:20:00IT运维版块每日发帖之星
日期:2016-07-27 06:20:00程序设计版块每日发帖之星
日期:2016-08-18 06:20:00
发表于 2016-06-27 14:33 |显示全部楼层
byte内的bitorder确实是编译器在蛋疼
byte内的bitorder在传输一个byte的时候才有意义(需要收发双方一致),但是这个对搞软件的来说,不用管

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2016-06-27 21:22 |显示全部楼层
回复 1# karma303
总结的很好,感谢分享。


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP