32-64升级注意事项
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
32bit-64bit porting work注意事项
64位服务器逐步普及,各条产品线对64位升级的需求也不断加大。在本文中,主要讨论向64位平台移植现有32位代码时,应注意的一些细小问题。
什么样的程序需要升级到64位?
理论上说,64位的操作系统,对32位的程序具有良好的兼容性,即使全部换成64位平台,依然可以良好的运行32位的程序。因此,许多目前在32位平台上运行良好的程序也许不必移植,有选择,有甄别的进行模块的升级,对我们工作的展开,是有帮助的。
什么样的程序需要升级到64位呢?
除非程序有以下要求:
●需要多于4GB的内存。
●使用的文件大小常大于2GB。
●密集浮点运算,需要利用64位架构的优势。
●能从64位平台的优化数学库中受益。
ILP32和LP64数据模型
32位环境涉及"ILP32"数据模型,是因为C数据类型为32位的int、long、指针。而64位环境使用不同的数据模型,此时的long和指针已为64位,故称作"LP64"数据模型。下面
由上表我们可以看出,32位到64位的porting工作,主要就是处理长度变化所引发的各种问题。在32位平台上很多正确的操作,在64位平台上都不再成立。例如:long->int等,会出现截断问题等。下面将详细阐述具体遇到的问题,并给出修改策略。
截断问题
截断问题是在32-64porting工作中最容易遇到的问题。
部分的截断问题能够被编译器捕捉到,采用-Wall –W进行编译,永远没有坏处。这种问题处理方法也非常简单,举个例子来说:
long mylong;
(void) scanf("%d", &mylong);// warning: int format, different type arg (arg 2)
long mylong;
(void) scanf("%ld", &mylong);// ok
但有很多情况下,一些截断性问题并不能被良好的诊断出来。
例如:
long a;
int b;
b = a;
在这种情况下,编译器会直接进行转换(截断处理),编译阶段不报任何警告。当a的数据范围在2G范围内时,不会出问题,但是超出范围,数据将出现问题。
另外,采用了强制转换的方式,使一些隐患被保留了下来,例如:
long mylong;
(void) scanf("%d",(int*)&mylong);//编译成功,但mylong的高位未被赋值,有可能导致问题。
采用pclint可以有效的检查这种问题,但是,在繁多的warning 中,找到需要的warning,并不是一件容易的事情。
因此,在做平台移植的时候,对于截断问题,最根本的还是逐行阅读代码,详细检测。
在编码设计的时候,尽量保持使用变量类型的一致性,避免发生截断问题。
建议:在接口以及数据结构的定义中不要使用指针,long,以及用long定义的类型(size_t, ssize_t, off_t, time_t),由于字长的变化,这些类型不能32/64位兼容。
一个讨厌的类型size_t:在32bit平台上,它的原形是unsigned int,而在64bit平台上,它的原形式unsigned long。这导致在printf等使用时:无论使用%u或者%lu都会有一个平台报warning。目前我们的解决办法是:采用%lu打印,并且size_t强制转换为unsinged long。在小尾字节序(Little-endian)的系统中,这种转换是安全的。
常量有效性问题
那些以十六进制或二进制表示的常量,通常都是32位的。例如,无符号32位常量0xFFFFFFFF通常用来测试是否为-1;
#define INV ALID_POINTER_V ALUE 0xFFFFFFFF
然而,在64位系统中,这个值不是-1,而是4294967295;在64位系统中,-1正确的值应为0xFFFFFFFFFFFFFFFF。要避免这个问题,在声明常量时,使用const,并且带上signed 或unsigned。
例如:
const signed int INV ALID_POINTER_V ALUE = 0xFFFFFFFF;
上面一行代码将会在32位和64位系统上都运行正常。或者,根据需要适当地使用“L”
或“U”来声明整型常量。
又比如对最高位的设置,通常我们的做法是定义如下的常量0x80000000,但是可移植性更好的方法是使用一个位移表达式:1L << ((sizeof(long) * 8) - 1);
参数问题
在参数的数据类型是由函数原型定义的情况中,参数应该根据标准规则转换成这种类型
。在参数类型没有指定的情况中,参数会被转换成更大的类型
。在64 位系统上,整型被转换成64 位的整型值,单精度的浮点类型被转换成双精度的浮点类型
。如果返回值没有指定,那么函数的缺省返回值是int 类型的
。避免将有符号整型和无符号整型的和作为long 类型传递(见符号扩展问题)
请看下面的例子:
long function (long l);
int main () {
int i = -2;
unsigned k = 1U;
long n = function (i + k);
}
上面这段代码在 64 位系统上会失败,因为表达式 (i + k) 是一个无符号的 32 位表达式,在将其转换成 long 类型时,符号并没有得到扩展。解决方案是将一个操作数强制转换成 64 位的类型。
扩充问题(指针范围越界)
扩充问题与代码截断问题刚好相反。请看下面的例子:
int baidu_gunzip(char* inbuf,int len,char* outbuf,int* size)
{
……
ret=bd_uncompress((Byte*)outbuf,(uLongf*)size,
(Byte*)(inbuf+beginpos),inlen);
}
这是ullib库中baidugz模块的一段代码。在这段代码中,将int型的指针改为long型的指针传递给了bd_uncompress函数。在32位系统中,由于int与long都是32bit,程序没有任何问题,但在64位系统中,将导致指针控制的范围在调用函数中扩展为原来的2倍,这将有可能导致程序出core,或指示的值不正确。这种问题比较隐蔽,很难发现,但危害极大,需要严格注意。
解决方法:加强对指针型参数的检查,看是否有范围扩充的问题。