1、刚工作时做Linux 流控;后来做安全操作系统;再后来做操作系统加固;现在做TCP 加速。唉!没离开过类Unix!!!但是水平有限。。
全部博文(353)
分类: LINUX
2015-06-04 12:31:45
原文地址:Linux GCC 64位编程技巧 作者:hanwei_1049
http://blog.csdn.net/yunhua_lee/article/details/5947173
========================================================================
既然要采用64位系统,首先要知道64位系统的优势所在。对于技术人员来说,完全没有必要去看那些厂家拿出的厚厚的说明书、或者某个研究机构抛出的一堆的数字,64位系统的优势总结起来很简单:内存大、速度快!
与32位系统相比,64位系统的地址空间大大增大,达到了18PB,18PB究竟是多大呢?说出来有点吓人:4G内存的40亿倍!这么大的空间,不要说内存了,就是整个磁盘的数据都放进去也是没有任何问题的。
需要注意的是:已有的32位系统由于采用了物理地址扩展技术(PAE, ),使得操作系统可用物理内存能够超过4G,但对于单个程序来说,能够使用的内存(即地址空间)还是只有4G,这个是无法突破的。
你可能很容易就推断如下结论:64位系统速度应该比32位快,而且应该是快两倍!
但实际情况可能让你有点吃惊:64位和速度没有绝对的比例关系,甚至可能速度更低。
64位架构处理器就本身而言,不会在速度上两倍于32位的同等处理器。为什么呢?简单来说:所谓的64位,只是和内存地址空间有关,和CPU速度没有一点关系,更不是64/32=2倍的关系。
另外,如果程序不需要对大量的数据进行处理,64位反而可能变慢,因为64位的地址是8位,32位的地址是4位,如果处理器的缓存是一样大小,那么很明显64位处理器能够缓存的东西是32位的一半,这反而降低了缓存命中率,因此影响了性能。
既然这样,那我们为什么还要说64位会更快呢?
我们知道,决定性能的不单是CPU,还有内存、磁盘、网络等一大堆东东,而64位机器在性能上改善最大的就是内存容量大大增加,从4G扩展到18PB,这就意味着内存中可以放很多数据,避免频繁的磁盘读写IO,从而大大提高性能;同时也意味着同一时间可以处理更多的数据,这也能够大大提高性能,尤其是多核多CPU并行处理的时候。
CPU本身结构的变化也会带来性能上的提升,这个提升主要体现在处理超过32位的整形数据上。对于32位系统,为了处理超过32位的整形数字,需要4次额外的寄存器操作;而64位系统,不需要这4次额外的寄存器操作。
某些CPU可能会为64位运算提供一些特定的特性,这也会带来一些特定处理上的性能提升,不过这种提升和具体的CPU相关,不是通用的特性。相关信息可以参考:。
64位虽好,但并不是放之四海皆准,毕竟从已有的32位升级到64位是要银子的,如果你写个“Hello, world!”也一定要求放到64位系统上来提高性能,那完全是浪费!
如下情况是必须用到64位系统的情况:
1. 程序(不是整个系统)需要超过4G的内存地址空间;
2. 使用libkvm库,或者/dev/mem,/dev/kmem的文件;
3. 使用/proc调试64位进程;
4. 使用只有64位版本的库;
5. 需要64位寄存器来更高效的进行64位运算,例如处理long long类型数据;
6. 使用超过2G的文件;
如果你是使用Java/Python等跨平台的语言进行开发,那么恭喜你,所谓的64位和32位对你来说没有差别,因为底层的虚拟机已经屏蔽掉了这种差异,在语言的层面是不需要理解这种差异的(这也是跨平台的一个原因吧)。
但如果你是用C/C++,那就有点郁闷了:C/C++是和系统强相关的,你在32位机器上写的代码,拿到64位机器上运行,可能出现你意料之外的结果,甚至可能崩溃,而且你还很难定位!
问题虽然很严重,而原因很简单:32位系统使用的数据模型是ILP32,而64位系统使用的数据模型是LP64或者LLP64.
ILP32:指的是int, long, pointer长度是32位,取首字母合起来就是ILP32(下面的简写都是这样的),windows和Unix类32位系统都是这种模型;
LP64:指的是long, pointer是64位,这个是Unix类64位系统采用的数据模型;
LLP64:指的是long long, pointer是64位,而long还是32位,这是Windows的64位系统采用的模型;
除了这个最主要的区别外,另外一个区别就是有的库可能只有64位版本,但不会只有32位版本,因为64位是支持所有32位的库的。
既然64位和32位开发环境主要的差别是数据模型的不同,那么最简单的一个方法就是尽力避开这种差异:咱们不用long类型了,管你32位还是64位,惹不起还躲不起么?
如果一定要用长整形,也还是不要用long,直接用__int64_t,如果你觉得写起来麻烦,那就自己定义为LLONG:typedef __int64_t LLONG即可
long类型可以不用,但指针没有办法不用,那就只能勇敢的面对了:)
1.指针打印:在使用printf的时候,指针打印控制符是%p,不要用%d或者%i.
2.指针转换:将指针转换为整形数据的时候,使用intptr_t,不要使用int。
如果用C++的stream流,则不存在这些问题,直接输出即可。
不管任何时候,都使用sizeof来确定变量或者类型的长度。
例如:对于结构来说,64位系统默认对齐的长度是8字节,而32位默认是4字节,因此在使用结构长度的时候,不能将长度写死,而要使用sizeof计算。
当你写下long j = 1 << 32;的时候,你是否以为系统会将1识别为long类型,然后给j赋值为2的32次方?
然而系统却在这里设下了一个陷阱:如果你不指定常量类型的话,常量默认就是int类型的!
因此不管你是32位还是64位,j的值都是0。
要想避开这个陷阱,就要指定常量的类型(常量也是有类型的),因此要写成:long j = 1L << 32;
常见常量类型:l/L,ll/LL,ul/UL,ull/ULL
当负整数转换为更长类型的无符号整数时,首先是转换为目标类型对应的有符号类型,然后再转换为无符号类型。
例如:
int i = -1;
unsigned long j = i;
则-1首先转换为signed long型,再转换为unsigned long型.
在这个转换过程中,有一个小小的陷阱:当从短的数据类型转到长的数据类型的时候,为了保证负数的有效性,会在多于的比特位填充1(因为负数的二进制码是补码,只有补1才能保证负数的符号和取值都不会变).
例如:
int i = 0x80000000; //对应十进制的有符号数-2147483648或者无符号数2147483648
unsigned long j = i;
你可能以为j会直接等于0x0000000080000000,但事实上j会等于0xffffffff800000000;
C/C++的库为了支持跨32位和64位,很多函数返回值都是***_t,例如sizeof操作,这种***_t实际上就是随32位或64位而变化的整形数。
1. 如果要使用返回值为***_t的函数,则变量也直接声明为***_t;
2. 如果要打印***_t的变量,gcc可以使用%Zu, %Zd;或者直接将变量转换为更长类型的数据后再打印,例如long或者long long,使用%lu, %llu, %ld, %lld控制符进行控制。
如果有的类型,如果你根本不知道它到底有多长,或者到底是什么类型,那么最简单的方式就是将其转换为一个最长的数据:整形当然是long long了,如果你连是否有符号都不知道,那就转换为double吧。
无论在32位还是64位系统下编译,-Wall选项都打开,虽然这不能保证100%能够发现所有问题,哪怕只能解决一个,也能够帮助你大大减少自己定位问题的时间。
64位编程军规:
sign extension:
RHEL5相关数据类型以及长度一览表: