X86平台Linux 32bit和64bit编程注意事项

时间:2020-12-30 02:53:48

http://www.ibm.com/developerworks/cn/linux/l-pow-inteltopwr/index.html

关于作者:Calvin Sze 是 IBM eServer Solutions Enablement 组织的一名 Linux 顾问。他在得克萨斯州的奥斯汀市工作。Calvin 的主要职责是帮助解决方案开发人员将他们的应用程序引入 Linux on POWER。Calvin 致力于 Linux 和 AIX 平台上的软件开发和系统集成已经 10 多年了。您可以通过 calvins@us.ibm.com 与 Calvin 联系。

 

32 位环境和 64 位环境中数据类型的长度

Linux 操作系统上的 GCC 和 XL C/C++ 编译器都提供两种不同的编程模型:ILP32 和 LP64。ILP32 代表 integer/long/pointer 32,这是 Linux 上的 32 位编程环境。ILP32 数据模型提供了 32 位的地址空间,其理论内存上限是 4 GB。LP64 代表 long/pointer 64,这是 Linux 上的 64 位编程环境。表 1 给出了在 POWER 和 x86 平台中 ILP32 和 LP64 模型中基本数据类型的宽度。
表 1. POWER 和 x86 平台中 ILP32 和 LP64 模型中基本数据类型的宽度(位数)

POWER x86
ILP32 ILP64 ILP32 ILP64
char 8 8 8 8
short 16 16 16 16
int 32 32 32 32
float 32 32 32 32
long 32 64 32 64
long long 64 64 64 64
double 64 64 64 64
long double 64/128* 64/128* 96 128
pointer 32 64 32 64

* 在 Linux on POWER 中,long double 缺省为 64 位。如果您使用 XL C/C++ 编译器的 -qlongdouble 选项,可以将其设置为 128 位的。

所有有关数字类型的定义在 POWER 和 x86 平台上都可以在 /usr/include/limits.h 中找到。

现在大部分 x86 平台上的 Linux 应用程序都是以 32 位模式运行的。然而,随着 x86 64 位扩展的出现(x86-64),越来越多的程序将会直接编写为 64 位的。在将 x86 应用程序移植到 POWER 平台上时,您应该使用与源环境匹配的 Linux POWER 环境。换言之,在迁移到 POWER 平台上的过程中,要避免从一个 32 位编程模型迁移到 64 位的编程模型,因为这种尝试并不是单纯的移植,而是一次开发任务。如果您将一个 32 位的 x86 应用程序移植到 64 位 POWER 编程模型中,那么可以将这次迁移分为两个步骤:

  1. 移植到 Linux on POWER 的 32 位环境中(包括测试和验证)。
  2. 迁移到 64 位环境中。

这就是说,如果一个程序可以满足以下条件,就应该被移植到 64 位环境中:

  • 可以利用超过 4 GB 的虚拟地址空间。
  • 可以利用更多的物理内存(超过 4 GB),如果用户希望将其部署到一个具有多于 4 GB 物理内存的系统上。
  • 可以利用 64 位的 long integer 类型。
  • 可以利用全部的 64 位寄存器实现更有效的 64 位数学计算。
  • 使用大于 2 GB 的文件。

可以从迁移到 64 位程序中获益的一些程序包括:

  • 数据库应用程序,尤其是那些进行数据挖掘的程序
  • Web 缓存和 Web 搜索引擎
  • CAD/CAE 模拟和建模工具的组件
  • 科学计算程序,例如流体力学、遗传仿真

一个程序可以保持是 32 位的,但仍然可以在 64 位的 Linux on POWER 内核上运行,而不需要修改代码。IBM 基于 POWER 处理器的服务器上的 Linux 可以支持在 64 位体系结构上同时运行 32 位和 64 位的应用程序,而不会造成这两种模式的性能降低,这是因为 64 位的 POWER 体系结构中包含了对本地 32 位模式的完全支持。

在不同的平台(从 x86 到 POWER)或编程模式(从 ILP32 到 LP64)之间移植程序时,您应该考虑不同环境中数据宽度和对齐设置的不同,这样才能防止出现可能的性能降低或数据崩溃的问题。

在从 x86 ILP32 移植到 POWER ILP32 上或从 x86 LP64 移植到 POWER LP64 上时,注意在 表 1 中,所有的基本数据类型的宽度都是相同的,long double 例外,对于 64/128 位的 IPL32 来说,它是 96 位;而对于 64/128 位的 LP64 来说,它是 128 位。这意味着您应该尽可能地检查以下代码中与 long double 数据类型有关的地方。如果您计划要使用 XL C/C++ 编译器,请使用-qlongdouble 编译标记来保证 long double 数据类型具有最好的可移植性。

数据对齐

在不同的平台或 32 位和 64 位模式之间移植程序时,要考虑不同环境中对齐设置的不同,从而避免出现可能的性能降低和数据崩溃的问题。最好的实践方法是确保数据都是自然对齐的。自然对齐的意思是说将数据项存储在是其大小的整数倍的地址处(例如,8 字节的数据的地址就应该是 8 的整数倍)。对于 XL C/C++ 编译器来说,聚集类型(C/C++ 结构/联合和 C++ 类)中的每种数据类型都会根据 linuxppc 或位填充规则按照字节边界进行对齐,其中 linuxppc 是缺省值,它是自然对齐的。linuxppc 也是与缺省的 GCC 对齐规则兼容的。表 2 显式了 POWER 和 x86 上的对齐值,以及它们的数据类型的宽度(以字节为单位)。


表 2. POWER 和 x86 上的对齐值(以字节为单位)
POWER x86
ILP32 ILP64 ILP32 ILP64
宽度 对齐 宽度 对齐 宽度 对齐 宽度 对齐
char 1 1 1 1 1 1 1 1
short 2 2 2 2 2 2 2 2
int 4 4 4 4 4 4 4 4
float 4 4 4 4 4 4 4 4
long 4 4 8 8 4 4 8 8
long long 8 8 8 8 8 8 8 8
double 8 8 8 8 8 8 8 8
long double 8/16 8 8/16 8 12 4 16 16
pointer 4 4 8 8 4 4 8 8

GCC 和 XL C/C++ 中的关键字 __alignof__ 让您可以了解一个对象是如何对齐的。它的语法与sizeof 类似。例如,如果目标及其要求一个 double 类型的值按照 8 字节边界进行对齐,那么__alignof__ (double) 就是 8。

正如在 表 2 中介绍的一样,long double 类型的变量在 x86 平台上是按照 4 个字节进行对齐的,而在 POWER 平台上则是按照 8 个字节进行对齐的。因此,在不同的平台上,这种结构就有不同的布局。不要将大小和偏移量都在编码中写死了,这一点非常重要。相反,使用 C 语言中的sizeof 操作可以查询基本类型和复杂类型的大小。宏 offsetof 是一个变量,它可以获取结构程序从该结构开始地址处的偏移量。