https://www.ibm.com/developerworks/cn/linux/l-port-linux-on-x86-application/
将 Linux on x86 应用程序移植到 Linux on Power 的指南
2014 年 8 月 08 日
本文将介绍如何使用以下直观的分步流程将 Linux® C/C++ 应用程序从 x86 平台(Intel® 或 AMD)移植到 IBM® PowerLinux™。我们首先要了解需要为移植做的准备工作,然后我们将介绍一些让 32 位和 64 位代码在 PowerLinux 上运行的实现技巧。简介
在大多数情况下,将 Linux 应用程序从 x86 平台移植到 Linux on Power 很简单,因为两个平台都基于来自 Novell SUSE 或 Red Hat 的同一个 Linux 版本。移植常常只需要执行一次 GNU Compiler Collection (GCC) 再编译,对一些编译器和链接器开关稍作更改。
但是,为解决突然出现的未知问题做好准备是拥有一个成功端口的重要优势。通常,如果重点关注的是移植,将优化放在以后进行,那么本文中介绍的指南、技术和工具可以最大程度地降低(甚至可能消除)移植到 Linux on Power 的难度。
在某些情况下,当您单独在一种特定硬件架构(比如 x86)上设计应用程序时,有时可能需要做一些额外的修改。本文会重点介绍与将 x86 系统上运行的 LInux 应用程序移植到基于 IBM POWER® 处理器的系统相关的一些差异,本文还提供了让 x86 代码为移植到 Linux on Power 做好准备的一些建议。
- 您应用程序的字节顺序
- 32 位和 64 位数据类型(长度和对齐方式)
- 理解可用的编译器选择 - GCC(分发和 Advance Toolchain)和 IBM XL C/C++ 编译器。
- 使用 IBM SDK for Linux 和 IBM Rational Developer for Power Systems - 使用 GNU C/C++ 编译器构建大型程序。
应用程序开发人员可以在 Power Linux 上使用一些高级工具和技术,其中包括 IBM Software Development Kit for PowerLinux (SDK),这是一个免费、基于 Eclipse 的集成开发环境 (IDE)。该 SDK 集成了 C/C++ 源代码开发与 Advance Toolchain、链接后优化和经典的 LInux 性能分析工具(包括 OProfile、Perf 和 Valgrind)。
计划移植
将应用程序移植到一个新平台时,恰当的计划不可或缺。要为移植做好充足的准备,您应该:
- 以可视方式扫描代码,尤其需要注意那些可能错误匹配的赋值运算,以及位操作和比较。
- 理解 IBM Power® 平台架构要点,以及 x86 与 IBM POWER 处理器架构之间的差异 - 特别是您应用程序的字节顺序。
- 确定使用哪个 Linux on Power 发行版:Red Hat Enterprise Linux 或 Novell SUSE Linux、CentOS 还是 Ubuntu。
- 考虑使用 GNU Make 构建系统。
- 确定要使用哪个编译器:GCC 或 IBM XL C/C++ for Linux。
- 获取一个用于开发的 IBM Power 服务器。下面列出了一些选择:
- 查看下面的程序,确定是否有个程序可以帮助加快移植并取得成功。
Power Development Cloud
这个完全自动化的 IBM Power Development Cloud 是一个免费的平台即服务 (PaaS),可用于使用 IBM 平台开发和演示软件解决方案。您可以利用 IBM Linux on Power 开发堆栈,它已预先配置和安装了针对从 x86 移植到 Linux on Power 的开发工具。
IBM Hardware Mall – 租赁和折扣
Hardware Mall 旨在激励独立软件供应商 (ISV) 在 IBM 平台上开发软件解决方案,为他们提供了系统硬件和适用软件的重要购买折扣和低租赁费率。
IBM Systems Application Advantage for Linux (Chiphopper)
IBM Chiphopper™ 产品是一个应用程序移植或重新托管程序,旨在免费帮助 IBM 业务合作伙伴启动、测试和支持其在竞争平台上运行的现有 Linux 应用程序向运行 LInux 和中间件平台的 IBM Power Systems™ 的移植。
IBM Innovation Center
IBM Innovation Center 提供了培训和一对一指导,涉及从构建到营销和销售解决方案的方方面面。IBM Innovation Center 团队随时准备帮助您实现开发目标
您可以在 解决方案开发硬件 网站上找到并利用所有这些服务。
理解 Power 平台的差异
您要移植到的 Power 硬件平台,确定了您在编译应用程序时想要使用的优化选项。您应考虑以哪个处理器版本为基础,例如 IBM POWER5、IBM POWER6®、IBM POWER7® 或 IBM POWER8™。为基于 POWER 处理器的系统开发一般性的代码非常容易,最终的应用程序将在多个系统上正常运行。针对 IBM 较新的几代 POWER 产品开发应用程序也很容易。一个重要的选择是选择使用 GCC 编译器还是 XL C/C++ 编译器。更多细节将在本文后面讨论。
编译器:
多年来,GCC 技术已得到显著改进,能够为基于 IBM POWER7 和 POWER8 处理器的服务器提供经过调优的应用程序。此外,如果性能是终极目标,那么 IBM XL 编译器可以实现更高的性能。
- 如果在 Linux on x86 上使用 GCC,推荐使用 GCC 完成初始移植。Linux on Power 上的 GCC 技术已显著改进,熟悉 GCC 会使得以它为基础进行移植变得更轻松。
- 如果需要的话,您还可以使用 IBM XL C/C++ 编译器。
- 如果在 Linux on x86 上使用 Java™,那么推荐从 IBM 网站下载并使用合适的 IBM Java 工具包。
有关 Power Systems 上的虚拟化的更多信息,请查阅 Linux on Power:针对开发人员的概述(developerWorks,于 2011 年 7 月更新)或参阅 执行简报 了解使用 Power Systems 进行服务器整合的可能性。
编译器标志:
移植到基于 POWER 处理器的服务器时,可以在 XL C/C++ 编译器中对 -qarch
和 -qtune
使用不同标志,或者在 GCC 编译器编译器中对 -mcpu
和 -mtune
使用不同标志来优化应用程序。但是,对于初始移植,为了最大限度降低难度,最好使用基于 Power 的最常见的 –qtune=ppc
和 –mcpu=power
。具体的标志和使用它们的时机将在本文后面讨论。
确定使用哪个 Linux on Power 发行版
来自 Novell 和 Red Hat 的最新的操作系统产品(本文发表时)提供了完成企业级部署所需的技术、支持和可靠性。首选产品的选择通常在一个公司或组织内要考虑的事项。
- SUSE Linux Enterprise Server (SLES)。有关 SLES 的更多信息,请参阅 参考资料 部分。
- Red Hat Enterprise Linux (RHEL)。有关 RHEL 或 CentOS 的更多信息,请参阅 参考资料 部分。
使用 SLES 还是 RHEL 的决定不会对移植过程产生直接影响。但是,SUSE 和 Red Hat 拥有不同的发布和更新周期,在二进制兼容性上具有不同的策略,从长远角度讲,这些可能影响您的应用程序更新决策。此外,Red Hat 和 SUSE 发行版按相同的时间表发布对 Intel® 和 Power 的支持。
迁移到 GNU Make
如果目前尚未使用 GNU Make 构建应用程序,那么您可以考虑迁移到该应用程序。一种不错的编程实践是:使用一个能控制可执行文件的生成的工具,而不依赖于脚本或直接调用编译器来生成可执行文件。大多数 C 和 C++ 编程人员都使用 Make 作为这样的工具。切换到 GNU Make,这样您就能通过同一个构建控件 makefile 在多个平台上执行一致的构建操作。GNU Make 同时具有 SLES11 和 RHEL6 版本。有关 GNU Make 的更多信息,请参阅 “参考资料” 部分。
假设您的 Linux on x86 应用程序是使用 GCC 构建的,那么我们推荐首先使用 GCC 在 Power 上重新编译的简单方法。后续步骤将会添加简单的调优和优化选项供考虑。如果需要的话,接下来的一个步骤将是考虑 IBM XL 编译器,以便实现针对特定 IBM Power Architecture® 级别调优的更高性能。
理解 x86 与 Power Architecture 衍生产品之间的差异
在将 x86 Linux 应用程序移植到 Power Systems 之前,您应该知道一些特定于架构的差异。在接下来的两节中,我们将详细介绍一些特别值得注意的架构差异:
- 字节顺序
- 32 位和 64 位应用程序环境中的数据类型长度
- 架构中的数据对齐方式差异
我们稍后将会介绍对比 x86 和 Power 应用程序时的一些额外的考虑因素。优化考虑因素示例包括使用多个线程优化吞吐量,评估简单锁定模式的使用,等等。
字节顺序
尽管 POWER 处理器可同时支持高位优先和低位优先(字节排序模式),但最新的实现都使用一种高位优先架构,而 x86 处理器使用了一种低位优先架构。本节介绍字节顺序问题,以及处理它们的技术。在将低级 应用程序、设备驱动程序或数据文件从 x86 架构移植到 Power Architecture 期间,开发人员通常会遇到一些字节排序问题。高级 应用程序在移植时很少遇到字节顺序问题,但在从 x86 移植到 Power 时需要解决此问题。
字节排序会影响整数和浮点数据,但不会影响字符串,因为它们会保持编程人员看到和想要的字符串顺序。所以,原始 ASCII 文本文件可正常使用,但与机器独立的数据文件可能受到硬件字节顺序问题的影响。例如,JPEG 文件被存储为高位优先格式,而 GIF 和 BMP 文件被存储为低位优先格式。
高位优先和低位优先
字节顺序指数据元素和它的各个字节在内存中存储和寻址的方式。在多位数中,具有更高数量级的位被视为更重要的位。例如,在 4 位数 8472 中,4 比 7 更重要。类似地,在多字节数字数据中,字节持有的值越大,它就越重要。例如,十六进制值 0x89ABCDEF 可拆分为 4 个字节:0x89、0xAB、0xCD 和 0xEF,分别具有算术值 0x89000000、0xAB0000、0xCD00 和 0xEF。显然,字节 0x89 是最大的值;因此它是最重要的字节,而字节 0xEF 是最小的部分,因此是最不重要的字节。
将一个数字放在内存中时,从最低的地址开始,只有两个有意义的选项:
- 首先放置最不重要的字节(低位优先)。
- 首先放置最重要的字节(高位优先)。
下图显示了高位优先和低位优先处理器如何将 32 位十六进制值(比如 0x89ABCDEF)放入内存中:
图 1. 存储十六进制值的高位优先和低位优先处理器
0x89 是最重要的字节,而 0xEF 是最不重要的字节。在高位优先系统上,最重要的字节放在最低的内存地址上。在低位优先系统上,最不重要的字节放在最低的内存地址上。
如果程序将一个单词写入内存中,然后读取相同位置作为一个单词,那么字节排序不存在问题,因为在采用一致的方式引用某个变量时,看到的会是同一个值。如果程序尝试一次读取同一个值的一个字节(在写入一个单词时),根据处理器采用低位优先还是高位优先格式,它可能获得不同的结果。
IBM POWER 处理器系列是使用高位优先数据布局的系统示例,而 x86 处理器系列是使用低位优先数据布局的系统示例。识别依赖于字节顺序的代码段,并将它们转换为等效的高位优先格式,这在将 x86 应用程序迁移到 Power 平台期间很重要。
处理字节顺序
本节将介绍如何识别代码中依赖于字节顺序的区域,并将它们转换为正确的字节顺序格式的方法。
依赖于字节顺序的代码
数据引用上的非一致性是 C 语言的优势,这使得它在编程系统级软件(包括操作系统和设备驱动程序)中变得很流行。此优势包括类型转换、指针操作、union、位字段、结构和灵活的类型检查。但是,同样是这些特性,它们也可能是字节顺序可移植性问题的来源。作为示例,请考虑以下两个代码清单:
清单 1. 使用指针的非一致数据引用
#include <stdio.h> int main(void) { int val; unsigned char *ptr; ptr = (char*) &val; val = 0x89ABCDEF; /* four bytes constant */ printf("%X.%X.%X.%X\n", ptr[0], ptr[1], ptr[2], ptr[3]); exit(0); }
清单 2. 使用 union 的非一致数据引用
#include <stdio.h> union { int val; unsigned char c[sizeof(int)]; } u; int main(void) { u.val = 0x89ABCDEF; /* four bytes constant */ printf("%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]); exit(0); }
在 x86 系统上,结果为:
EF.CD.AB.89
在基于 POWER 处理器的系统上,结果为:
89.AB.CD.EF
字节顺序问题表现为 val
,它从最重要的字节开始逐个字节地读取数据。
如何确定系统的字节顺序
在 Linux 中,GNU_C 预处理器通常会自动提供一组常用的预定义宏,它们的名称以双下划线开始和结束。一组对确定使用的系统的字节顺序最有用的宏是 __BYTE_ORDER__、__ORDER_LITTLE_ENDIAN__ 和 __ORDER_BIG_ENDIAN__。
/* Test for a big-endian machine */ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ /* Do big endian processing */
编写字节顺序中立的代码
如果程序模块在跨具有不同字节顺序的平台移植时能够保留其功能,那么它就是字节顺序中立的。换句话说,它的功能与运行它的平台的字节顺序无关。以下是一些编写字节顺序中立的代码的建议:
- 使用宏和指令
要让代码变得可移植,可以使用宏和条件编译指令,如清单 3 和清单 4 所示。
清单 3. 使用指令让字节顺序效果中立化
#include <stdio.h> #define BIG_ENDIAN 1 #define LITTLE_ENDIAN 0 #define BYTE_ORDER (( htonl(1)==1) ) // returns 1 or 0 depending on platform union { int val; unsigned char c[sizeof(int)]; }u; int main(void) { u.val = 0x89ABCDEF; #if (BYTE_ORDER == BIG_ENDIAN) printf("%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]); #else /*!BYTE_ORDER == BIG_ENDIAN*/ printf("%X.%X.%X.%X\n", u.c[3], u.c[2], u.c[1], u.c[0]); #endif /*BYTE_ORDER == BIG_ENDIAN*/ exit(0); }
清单 4. 使用宏交换字节(对在运行时确定字节顺序很有用)
// Useful Endian Neutral Macros #include <endian.h> #if __BYTE_ORDER == __BIG_ENDIAN // No translation needed for big endian system #define sw2Bytes(val) val #define sw4Bytes(val) val #define sw8Bytes(val) val #else // Little Endian:Translate // Swap 2 byte, 16 bit values: #define sw2Bytes(val) \ ( (((val) >> 8) & 0x00FF) | (((val) << 8) & 0xFF00) ) // Swap 4 byte, 32 bit values: #define sw4Bytes(val) \ ( (((val) >> 24) & 0x000000FF) | (((val) >> 8) & 0x0000FF00) | \ (((val) << 8) & 0x00FF0000) | (((val) << 24) & 0xFF000000) ) // Swap 8 byte, 64 bit values: #define sw8Bytes(val) \ ( (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) | \ (((val) >> 24) & 0x0000000000FF0000) | (((val) >> 8) & 0x00000000FF000000) | \ (((val) << 8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) | \ (((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000) ) #endif
int main(void) { int a=0x11121314; int b; b = sw4Bytes(a); // b is 0x12 in LE and BE }
- 使用编译时选项
实现此目的的另一种方式是在编译器命令行上将BYTE_ORDER
的值定义为-DBYTE_ORDER=BIG_ENDIAN
。在一个具有不同字节顺序的新平台上编译时,这会消除编辑设备驱动程序或应用程序中的每个文件的需求。只需编译用于构建该驱动程序或应用程序的 makefile。
字节顺序:特定于 Linux
事实证明,Linux 内核提供了一组特定的系统宏,它们可以从低位优先到高位优先和从高位优先到低位优先执行 16、32 和 64 位交换。这些宏对从 Linux x86 移植到 Linux on Power 以及让您大代码对字节顺序中立都很方便,无需担忧会在代码中编写大量 #ifdef __LITTLE_ENDIAN 条件指令。
例如:
cpu_to_le16(u16); // converts CPU endianness 4 bytes to little-endian
le16_to_cpu(u16); // converts little-endian 4 bytes to CPU endianness
这两个宏将一个值从 CPU 使用的字节顺序转换为一种无符号、低位优先、32 位数字,或者执行反向转换。例如,在 Linux on Power 系统上,le16_to_cpu(u16) 将低位优先转换为高位优先,而 cpu_to_le16(u16) 将高位优先转换为低位优先。根据使用的宏和运行的是哪个系统,如果没有工作要做,那么它们将会返回原始值。
这些宏和其他许多宏位于:/usr/include/linux/byteorder/big_endian.h> 和 /usr/include/linux/byteorder/little_endian.h 中。
您可以查看这些头文件来找到您需要的宏。您还可以根据名称中的模式推断出它们的用途。
您可以使用预定义的 GNU_C 预处理器指令,轻松地包含正确的 include 文件,如下所示:
#include <endian.h> #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #include <linux/byteorder/big_endian.h> #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #include <linux/byteorder/little_endian.h> #else // …user defined endian header #endif
其他字节顺序考虑因素
从 x86 移植到 Power 时,不依赖于外部数据文件并且遵守严格的 C ANSI 编程标准的高级应用程序或许能够顺利地移植到 Linux on Power,且不会出现字节顺序问题。但是,字节顺序是一个需要理解的重要概念,而且在代码中识别这些漏洞的能力能够使得向 Linux on Power 的迁移变得很容易。
此外,在下一节中,我们将重点介绍 IBM Linux SDK 中提供的其他代码分析工具,它们可以帮助识别字节顺序问题。
选择要使用哪些迁移工具
类似于机械领域,移植工程师的 “技术” 工具箱中必须有许多工具,而且知道使用哪个工具可用来最大限度降低移植的难度。IBM Linux on Power SDK 和 IBM Rational Developer for Power Systems 都是独立、完整的集成开发环境 (IDE),可用于简化 Power 服务器上的移植和开发,是开发人员在移植到 Linux on Power 时可以使用的优秀技术工具。
IBM SDK for PowerLinux with Migration Advisor
IBM Software Development Kit for PowerLinux (SDK) 是一个免费、基于 Eclipse 的 IDE。该 SDK 集成了 C/C++ 源代码开发与 Advance Toolchain、链接后优化工具(比如 FDPR)和经典的 LInux 性能分析工具(包括 OProfile、Perf 和 Valgrind)。此外,IBM SDK for PowerLinux 中有一个叫做 Migration Advisor 的工具。Migration Advisor 工具的一个特性是代码检查器 和代码修复 特性,该工具可以分析代码中的 Linux/x86 漏洞,为您提供快速修复代码中的漏洞的选项或者提供手动修复它的建议。例如,下面的转换是从 x86 Linux 移植到 Linux on Power 时一种典型的字节顺序潜在问题。消息 “Cast with endianness issue checker” 表示存在这样的漏洞。
void foo() { short int val = 0xFF00; char *char_pointer = (char *) &val; //This prints 0 in x86 (or x86_64) machines and ff in ppc (or ppc64) machines. printf("%x\n", *char_pointer); }
Migration Advisor 使用 Eclipse CDT Code Analyzer (Codan) 源代码分析器来查找 C/C++ 源代码中的潜在迁移问题,比如可能在 x86 和 POWER 服务器上运行时生成不同结果的代码块。要查找源代码中的问题,Codan 会分析 C/C++ 源代码抽象语法树 (AST),查找可能与 Power Architecture 不兼容的代码段。在找到潜在问题时,Migration Advisor 会保存该问题在代码中的位置,并在源代码中的这个特定位置添加一条警告。目前,Migration Advisor 仅兼容 C/C++ 代码。
以下是在 IBM SDK for PowerLinux 中使用 Migration Advisor 的屏幕截图。
图 2. 运行 Migration Advisor ‘Union with Endianness Issues’ 检查器
Migration Advisor:快速修复特性
Migration Advisor 还为一些迁移问题提供了快速修复方法,能够将依赖于架构的代码块替换为与 POWER 处理器兼容的指令。可以通过两种方式触发快速修复:右键单击源代码中的警告并选择快速修复选项;或者打开 Migration Advisor Eclipse View,右键单击一个特定问题,然后选择快速修复选项。
Migration Advisor 所提供的快速修复仅适用于:
- 特定于 x86、内置的编译器的使用
- 内联程序集的使用
- 性能降级
您可以选择可激活或可停用的检查器。每次您修改源代码后,Migration Advisor 都将重新检查代码并更新结果,使您在对项目执行完成的重新构建之前有机会快速识别和解决问题。图 3 显示了 Migration Advisor 窗口,其中已激活所有检查器。
图 3. 激活所有检查器
激活 Migration Advisor 检查其后,右键单击项目并单击 Run Migration Advisor。然后,Migration Advisor 会使用启用的检查器分析代码,并在 Eclipse 视图中显示找到的潜在问题的结果(参见图 4)。
图 4. Migration Advisor 视图
图 5 显示了一个不受 Power 平台支持的内置插件所导致的一个真实迁移问题的示例。为了修复该问题,可以在 Migration Advisor Eclipse 视图中双击它。然后将打开源代码视图,突出显示问题的准确位置。如果将鼠标移动到突出显示的代码上方并按 Ctrl+1,则会打开另一个弹出窗口,如图 5 所示。
图 5. 使用 Migration Advisor 进行快速修复
选择第一个选项 Builtin quick fix。应用快速修复后的结果代码如图 6 所示。
图 6. 应用 Migration Advisor 快速修复后的代码
其他有用的 Migration Advisor 检查包括:
- 特定于 x86 的程序集检查器
- 包含位字段的结构的检查器
- Long double 使用检查器
- 性能降级检查器
- 特定于 x86 的编译器内置检查器的使用
关于这些方便的检查的完整列表,请参阅 IBM 知识中心。
Linux Cross-platform Tool (LCT)
SDK for PowerLinux 还提供了另一个可以在任何现有的 Intel x86 平台上运行的方便工具。它是一个命令行工具,用于识别应用程序的潜在可移植性。这样,在移植到 Linux on Power 之前,您能够快速了解源代码中的潜在问题。在安装 IBM SDK for PowerLinux 后,可以在该工具的 README 部分中找到 LCT 文档。
IBM Rational Developer for Power Systems
IBM Rational Developer for Power Systems 也提供了一个富桌面集成开发、移植和优化环境,用于多平台开发(IBM i、AIX 或 Linux on Power)。它的特性还包括各种移植工具,比如 Code Analysis 工具、Performance Advisor 工具和 Migration Assistant,类似于 IBM SDK for PowerLinux,这些工具可以检测 32 位到 64 位迁移问题、字节顺序问题,以及其他可能阻碍移植的操作系统问题。
JVM
Java 虚拟机 (JVM) 在所有平台上都以高位优先模式运行,因此常常对处理器架构带来的影响免疫。
数据类型和对齐方式
应用程序二进制接口 (ABI) 中重要的 有符号/无符号字符 细微差异。
在 x86/x86_64 下,“char” 的默认值为 “signed char”,而 Power Systems 上的默认值为 “unsigned char”。在 Power Systems 上,可以使用 GCC 指令 -fsigned-char
覆盖该默认值。等效的 IBM XL 指令为 -qchars=signed
。
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 默认值:x86 上为 signed - POWER 上为 unsigned | 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 |
指针 | 32 | 64 | 32 | 64 |
*Linux on Power 上的 long double 的默认大小现在为 128 位。如果使用编译器选项 -qnoldb128
和 XL C/C++ 编译器,那么它们可以减少到 64 位。
在 Power 和 x86 平台上的 /usr/include/limits.h 中,可以找到数字值的所有定义。
许多遗留 Linux x86 应用程序在 32 位下运行。对于最新的 x86 架构(支持并鼓励采用 64 位应用程序),更多的 x86 应用程序是在 64 位模式下原生地更新或编写的。在将 x86 应用程序移植到 Power Systems 的练习中,可采用 Linux on Power 环境作为目标来匹配您的来源环境。换句话说,我们推荐首先完成初始移植,然后再考虑移植到 64 位编程模型。如果希望将 32 位 x86 应用程序移植到 64 位 Power Systems 编程模型,那么可以将迁移分为两步:
- 移植到 Linux on Power 32 位环境(包括测试和验证它)。
- 然后迁移到 64 位环境。
如果应用程序满足以下条件:那么工程师应考虑将应用程序移植到 64 位:
- 能从超过 4GB 的虚拟地址空间受益
- 能够从更多物理内存(大于 4GB)受益,以及用户可能将它部署在具有超过 4GB 物理内存的系统上
- 能够从 64 位大小的长整数受益
- 能够使用完整的 64 位寄存器来执行高效的 64 位算法
- 使用大于 2GB 的文件
一些可通过迁移到 64 位而受益的应用程序示例包括:
- 数据库应用程序,尤其是执行数据挖掘的应用程序
- Web 缓存和 Web 搜索引擎
- 计算机辅助设计 (CAD)/计算机辅助工程 (CAE) 模拟和建模工具的组件
- 科学和技术计算应用程序,比如计算流体动力学、遗传模拟等
应用程序可保留 32 位并仍在 64 位 Linux on Power 内核上运行,无需任何代码更改。基于 IBM Power 处理器的服务器支持 32 位和 64 位应用程序同时在 64 位架构上运行
在不同平台(从 x86 到 Power)或编程模型(从 ILP32 到 LP64)之间移植应用程序时,需要考虑不同环境中提供的数据宽度和对齐设置之间的差异,以避免可能的性能降级和数据损坏。
在从 x86 ILP32 移植到 POWER ILP32 或从 x86 LP64 移植到 POWER LP64 时,您可能注意到,在表 1 中,所有基本数据类型的宽度都保持相同,除了 long double 96 位变为 IPL32 上的 64/128 位,128 位变为 LP64 上的 64/128 位。这意味着您应该检查与 long double 数据类型相关的代码部分。如果计划使用 XL C/C++ 编译器,那么可以使用 -qlongdouble
编译标志在 long double 数据类型上实现最高的兼容性。
数据对齐
在平台之间或 32 位与 64 位模型之间移植应用程序时,需要考虑不同环境中提供的对齐设置之间的差异,以避免可能的性能降级和数据损坏。最佳实践是对数据项执行自然对齐。自然对齐表示将数据项存储在是其大小的倍数的地址上(例如,8 字节数据项存储在一个为 8 的倍数的地址中)。对于 XL C/C++ 编译器,依据 linuxppc 或位包装规则,聚合中的每种数据类型(C/C++ 结构/union 和 C++ 类)将与字节边界对齐,其中 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 |
指针 | 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 Systems 上与 8 字节对齐。因此,不同平台上的结构将具有不同的布局。不要硬编码任何大小和偏移,这很重要。相反,您应该使用 C 运算符 sizeof
来查询基础和复杂类型的大小。宏 offsetof
可用于从结构开头获取结构成员的偏移。
确定要使用哪个编译器:GCC 或 IBM XL C/C++
有两个 C/C++ 编译器可用于 Linux on Power:GCC 和 IBM XL C/C++ 编译器。GCC 针对 Linux 上的编译提供了可靠的代码移植功能,而在使用更高级的优化时,与 GCC 相比,IBM XL 编译器显著提升了性能。两种编译器都提供了 32 位和 64 位编译模式,Linux on Power 环境支持在不降低性能的情况下同时运行 32 位和 64 位代码。
使用 GCC 编译器集进行移植
如果项目跨多个平台而开发,而且其中 GCC 编译器是原始的编译器,那么 GCC 编译器常常用于部署针对 Linux on Power 的应用程序。性能不那么关键的应用程序(比如小型实用程序)通常属于这种情况。GCC 还允许使用一些只有 GCC 能够理解的代码原型,比如特定于 GCC 的宏。但要注意的是,许多特定于 GCC 的特性都合并在 XL C/C++ 编译器中。
一般而言,使用 GCC 移植代码应该很简单。在大部分情况下,只需简单地重新编译和键入 make
命令即可。架构可能有所不同,而且有时可能存在库版本差异。但从很大程度上讲,它在哪个架构上运行并不重要。我们不鼓励您在 Power Linux 上编译特定于架构的标志,比如 -m486
和 -mpowerpc64
,因为 GCC 没有针对这些架构上的优化例程的丰富的处理器映射。同样地,如果不使用特定于架构的标志,那么不同 POWER 硬件模型上的二进制兼容性会更高。
SLES 11 和 RHEL 6 中包含的 GCC 编译器同时支持 32 位和 64 位应用程序。在= SLES 11 和 RHEL 6 版中,默认编译模式为 64 位。
在所有架构上,库必须使用 -fPIC
来编译;x86 默认情况下会在 GCC 中应用此标志。在 POWER 上,此标志指定将生成的代码用在一个共享对象中。请查阅 GCC 编译器手册,以便了解有关的更多信息(参见 “参考资料”)。
使用 IBM XL C/C++ 编译器进行移植
XL C/C++ 使用了 GNU C 和 C++ 头文件,而且结果应用程序被链接到 GCC 提供的 C 和 C++ 运行时库。这意味着 XL C/C++ 编译器生成了 GNU Executable and Linking Format (ELF) 对象,这些对象完全兼容 GCC 编译器生成的对象。XL C/C++ 包含对称多处理 (SMP) 运行时库,以便支持 XL C/C++ 的自动并行化和 OpenMP 特性。
从 GCC 移植到针对 Linux on Power 的 XL C/C++ 很简单。XL C/C++ 通过提供选项 -qinfo=por
来帮助完成此任务,帮助您过滤掉省略的诊断消息,进现实与可移植性问题相关的消息。此外,XL C/C++ 支持 gcc 和 gcc-c++ 的 GNU 扩展的一个子集。
请参阅 “XL C/C++ for Linux on pSeries 编译器参考”,了解受支持的特性的完整列表,以及那些被接受但拥有忽略了的语义的特性。
要对 C 代码使用受支持的特性,可以指定 -qlanglvl=extended
或 -qlanglvl=extc89
。在 C++ 中,默认情况下会接受所有受支持的 GNU gcc/gcc-c++ 特性。此外,gxlc 和 gxlc++ 有助于最小化对使用 GCC 编译器构建的现有应用程序的 makefile 的更改
XL C/C++ 中的优化选项
XL C/C++ 提供了一些专门针对 IBM 硬件的优化选项。对于 Linux on Power,与使用 GCC 编译的应用程序相比,许多使用 XL C/C++ 并利用正确的优化标志组合而编译的应用程序都有巨大的性能改进。请注意,不是所有优化对所有应用程序都有利。通常,您需要在编译器完成的优化程度与编译时间的增加和调试能力的降低之间进行权衡。
优化级别
优化级别由编译器选项来指定。下表总结了每个优化级别上的编译器行为。
表 3. 每个优化级别上的编译器行为
选项 | 行为 |
---|---|
-qnoopt | 提供快速编译和完全调试支持。 |
-O2(与 -O 相同) | 执行被编译器开发人员视为编译速度和运行时性能的最佳组合的优化。此设置会暗中使用 -qstrict 和 -qstrict_induction ,除非使用 -qnostrict_induction 和 -qnostrict 进行明确否定。 |
-O3 | 执行更多内存密集型和/或编译时间密集型的优化。在运行时改进比最小化编译资源的利用更重要时,推荐执行这些优化。 |
-O4 和 -O5 | 执行过程间优化、循环优化和自动机器调优。 |
应避免的旧 Power 选项
从 x86 移植到 Linux on Power 时,来自旧 Power Architecture 的一些选项应避免使用且有害。
- 使用
-mminimal-toc
或-mfull-toc
的 PPC64 构建版本应替换为-mcmodel=medium
(这是默认值) -ffloat-store
,不会在 Power 上使用
目标机器选项告知编译器生成能够在给定微处理器或架构家族上最优地执行的代码。通过选择合适的目标机器选项,可通过优化来适应最广泛的目标处理器、给定处理器架构家族的许多处理器,或者一个特定的处理器。以下选项会控制影响目标机器的各个方面的优化:
表 4. 影响目标机器的各个方面的优化选项
选项 | 行为 |
---|---|
-qarch | 选择应针对其生成指令代码的一个处理器架构家族。默认值为 -qarch=auto 。还提供了以下子选项:ppc64grsq、pwr3、pwr4、pwr5、ppc970、ppc64、ppcgr、rs64b 和 rs64c。 |
-qtune | 针对在给定微处理器上执行而进行的基础优化,没有暗示要用作目标的指令集架构的任何信息。Linux 上的默认值为 -qtune=auto 。可用的子选项包括:pwr3、pwr4、pwr5、pwr6、pwr7、pwr8、ppc970、rs64b 和 rs64c。但是,在许多情况下,此设置会基于 –qarch 的设置而自动设置。 |
-qcache | 定义一种特定的缓存或内存几何结构。如果使用了 -qcache ,则与它一起使用 -qhot 或 -qsmp 。 |
-qhot | 高阶转换属于专门通过交换 (interchange)、熔合 (fusion) 和展开 (unroll) 等技术来改善循环性能的优化。指定 -qhot 时,-qhot=vector 是默认选项。您可以尝试将 -qhot 与 -O2 和 -O3 结合使用。它的设计目标是在没有转换机会时产生一种中立效果。 |
-qsmp | 生成共享内存并行处理所需的多线程代码。指定 -qsmp 时,-qsmp=auto 是默认选项。如果您在编译一个 OpenMP 程序且不想要自动并行化,那么可以使用 -qsmp=omp:noauto 。使用 -qsmp 时始终使用 _r 编译器调用。 |
要最充分地利用目标机器选项,您应该:
- 使用
-qarch
,指定您希望代码能在其上良好运行的最小的机器系列。 - 使用
-qtune
,指定应具有最佳性能的机器。例如,如果将在基于 POWER8 处理器的系统及更高版本的系统的 Power 服务器上运行应用程序,那么可以使用-O3 -qarch=pwr8 -qtune=pwr8
。
IBM Power 平台支持其他平台上所没有的机器指令。XL C/C++ 提供了一组内置的函数,它们直接对应于某些 POWER 处理器指令。使用这些函数能消除函数调用-返回成本、参数传递、堆栈调整和所有其他与函数调用相关的成本。有关支持的内置函数的完整列表,请参阅 XL C/C++ C++ for Linux on pSeries 编译器参考 安装文档。
但是,在使用 IBM XL C/C++ 编译器重新编译时,最初打算使用 GCC 编译器编译的软件可能需要更多地进行关注。您需要编辑 makefile 来反映 XL C/C++ 编译器的正确路径,该路径默认为 /opt/ibmcmp/。还需要设置针对特定架构的正确的优化标志(比如 -03 -qarch=pwr8 -qtune=pwr8
用于 IBM POWER8 处理器)。
除了针对各种 Power Architecture 衍生产品的优化模式,还可以告诉 XL C/C++ 编译器在通用模式下编译软件。这可以确保跨所有 Power Architecture 衍生产品的兼容性,但会牺牲通过特定于架构的优化所获得的性能优势。在编译 64 位代码时,必须为编译器提供针对 64 位构建版本的标志(即 -q64
),因为它在默认情况下为 32 位模式。
下面列出了使用 XL C/C++ 编译器编译面向 GCC 的代码的一些技巧:
- 在默认情况下,GCC 允许在 C 文件中使用 C++ 风格的注释,但这不适合 XL C 编译器 XLC。因为更改一组源文件中的所有注释来遵守 C 风格的注释格式不太合算,所以 XLC 提供了一个
-q
参数来允许使用这些注释:-q cpluscmt
。在使用此标志编译 C 代码时,会解释 C 和 C++ 风格的注释。 - 环境变量常常是配置构建脚本的最简单的方式。无需手动编辑配置脚本和 makefile,您应该设置相关的环境变量,比如
$CC
和
$CFLAGS
,以便允许通过配置脚本来生成 makefile,而无需手动编辑。 - 配置脚本还需要知道正确的平台类型。平台类型将为 linux-powerpc-unknown-gnu 或 linux-powerpc64-unknown-gnu。您应该通过将
-target=
标志被附加到配置脚本中,为使用 GCC 或 XL C/C++ 的编译设置此平台类型。 - 尽管 IBM XL C/C++ 编译器有许多文档,您仍然可以运行该编译器而不使用参数在控制台显示参数列表,例如
$COMPILER_PATH/bin/cc
。
编译器选项比较
下表比较了来自 GCC 和 XL C/C++ 的常用编译器选项。
表 5. 来自 GCC 和 XL C/C++ 的常用编译器选项
GCC | XL C/C++ | 描述 |
---|---|---|
-v | -v、-V、-# | 打开详细模式。 |
-p/-profile | -p | 设置编译器生成的对象文件,以进行分析。 |
-m32、-m64 | -q32、-q64,或者设置 OBJECT_MODE 环境变量 | 创建 32 或 64 位对象 |
-fsyntax-only | -qsyntaxonly | 执行语法检查而不生成对象文件。 |
-fpic | -qpic=small | 生成与位置无关的代码,以便在共享库中使用它们。在 XL C/C++ 中,全局偏移表的大小不大于 64 KB。如果指定了 -qpic 而没有任何子选项,则假设使用了 -qpic=small 。如果指定了 -qmkshrobj 编译器选项,则会启用 -qpic 选项。 |
-fPIC | -qpic=large | 允许全局偏移表大于 64 KB。 |
-pthread | -qthreaded 或 _r invocation 模式 | 创建在多线程环境中运行的程序。 |
-fno-rtti | -qnortti | 禁止生成运行时类型标识 (RTTI) -qrtti 来进行异常处理,并且禁止 typeid 和 dynamic_cast 运算符使用 RTTI。在 XL C/C++ 上,默认选项为 -qnortti 。 |
-static | -qstaticlink | 阻止此选项生成的对象与共享库链接。 |
-static-libgcc | -qstaticlink=libgcc | 告诉编译器与 libgcc 的静态版本链接。 |
-shared | -qmkshrobj | 告诉编译器生成一个共享对象。 |
-shared-libgcc | -qnostaticlink=libgcc | 告诉编译器与 libgcc 的共享版本链接。 |
-Wl、-rpath | -Wl、-rpath 或 -R | 传递一个冒号分隔的目录列表,用它来指定一个运行时链接所搜索的目录。 |
-fno-implicit-templates、-frepo | -qtempinc、-qtemplateregistry、-qtemplaterecompile | 实例化模板。 |
-w | -w | 禁止警告消息。 |
-warn64 | 启用对长整型到整型的截断操作的检查。 | |
-qinfo=< > | 生成包含信息的消息。 | |
-fpack-struct | -qalign=bit_packed | 使用 bit_packed 对齐规则。 |
-qalign=linuxppc | 使用默认 GCC 对齐规则保持与 GCC 对象的兼容性。这是默认设置。 | |
-O、-O2、-O3 | -O、-O2、-O3、-O4、-O5 | 用于设置优化级别。 |
Ofast、Og | Ofast – 提供速度优化,不完全符合标准 Og – 提供优化,但允许在需要时执行一些有效的调试。 | |
-mcpu、-mtune | -qarch、-qtune、-qcache | 用于设置一个特定处理器的优化选项。 |
使用 GCC 构建大型程序
客户有时需要构建一个在运行时生成的非常大的可执行文件。清单 5 中给出了一条常见的错误消息。
清单 5. 构建大型可执行文件时的错误消息
modelfile.cxx.o:(.text+0x212012): relocation truncated to fit: R_PPC64_TOC16_DS against `.toc'+10000
这是大型程序和旧 GCC 编译器的一个常见问题。旧 GCC 编译器乐观地假设目录 (table of contents, TOC) 不会溢出单个加载指令的 64 KB 范围。在这种情况下,该程序很大,需要的所有 TOC 条目的总和超过 16 位 (TOC16_DS)。
有两个解决方案。
- 对于 Advance Toolchain 3.0 和旧编译器,可以使用
-mminimal-toc
进行重新编译。 - 对于 Advance Toolchain 4.0 和更高版本的编译器,可以使用
-mcmodel=medium
进行重新编译。-mcmodel=medium
应具有更好的性能,但需要升级。
有关的更多细节,请参阅 GCC PowerPC 选项。
移植步骤
完成每个计划步骤后,应该已经准备为执行移植做好了准备。本节将介绍成功将应用程序移植到 Linux on Power 的推荐步骤。
- 将构建系统迁移到 GNU Make(如果有必要)
这是构建一个或多个 makefile 的过程。您还可以利用 GNU 的程序构建自动化实用程序,比如 Autoconf、Automake 和 Buildtool,以便最大程度地提高您的程序跨不同 UNIX® 平台移植的能力。GNU 的程序构建自动化实用程序位于 directory.fsf.org/devel/build/。 - 修改依赖于架构的代码(如果有必要)
考虑字节顺序、32 位和 64 位模型下的数据长度,以及不同平台上的数据对齐方式,这些已经在 理解 x86 与 Power Architecture 衍生产品之间的差异 一节中介绍过。 - 在 构建
构建 makefile 并修改了所有程序后,构建流程很简单,只需发出一个命令,比如make
。如果在构建过程中遇到错误,这些错误通常是编译器和链接器错误,以及程序语法错误。通过 makefile 修改编译器选项或修改代码中的引起问题的语法,通常可以修复错误。编译器参考菜单和编程指南是此阶段的最佳参考资料。对于 IBM XL C/C++,请参阅 XL C/C++ for Linux 编译器参考 (compiler.pdf) 和 XL C/C++ for Linux 编程指南 (proguide.pdf)。对于 GCC,编译藏靠和编程指南都在 gcc.gnu.org/onlinedocs 上。 - 测试和问题排除
成功构建程序后,测试其中是否有运行时错误。运行时错误通常与此阶段的程序逻辑相关。编写多个测试程序来验证应用程序的输出是否满足预期,总是一种不错的想法。 - 调优性能
移植的代码已在 Power 平台上运行后,可监视它以确保它按预期执行。如果不符合预期,您需要完成性能调优。可使用以下工具套件识别应用程序中的性能问题,显示应用程序与 Linux 内核的交互情况。- OProfile
OProfile 工具分析基于硬件相关事件的代码,比如缓存遗漏理器周期。例如,OProfile 可帮助确定导致大部分缓存遗漏错误的来源例程。OProfile 使用了许多处理器中提供的硬件性能计数器,包括 IBM POWER6 和 POWER7。有关 OProfile for Power Linux 的更多信息,请访问 OProfile 网站(参见 参考资料)。 - 链接后优化 - 也称为 FDPRpro
链接后优化工具通过在程序用于执行一些典型工作负载时收集程序的行为信息,优化该程序的可执行镜像。然后,它会重新分析该程序(结合收集的概要文件),应用全局优化(包括程序重构),以及创建一个针对该工作负载而优化的新程序版本。该优化器生成的新程序通常比原始程序运行得更快,使用的真实内存更少。有关更多信息,请访问链接后优化网站(参见 参考资料)。
- OProfile
- 包
如果移植的应用程序将是一个商业产品,或者想要将该应用程序分发给第三方来安装,那么您需要打包移植的应用程序,包含库、文档,有时还包含源代码。Linux 提供了多种方式来打包应用程序,比如 .tar 文件、自安装的 shell 脚本,以及 RPM。RPM 是用于 Linux 的最流行的打包工具之一。
图 7. 上述 1 至 6 步中描述的移植活动流
结束语
Linux on Power 提供了一个企业级 Linux 环境,其中同时包含 32 位和 64 位应用程序环境和工具链。Linux on Power 提供了两组编译器,它们简化了开源代码的移植和对屡获殊荣的 Power Architecture 的高性能的利用。通过将 Linux on x86 应用程序移植到 Linux on Power,您可以利用 Power Architecture 上提供的应用程序性能,这一性能现在还得到了 Linux 操作系统上之前从未提供过的开发工具的增强。总之,Linux on Power 是一个部署高性能 Linux 应用程序的领先平台。
致谢
感谢 Calvin Sze 提供原始文章,感谢 Linda Kinnunen 提供原始文章模板和很有帮助的评审,感谢 IBM Linux on Power 团队提供技术帮助和审阅本文。
感谢 Otavio Busatto Pontes 针对新的 IBM SDK for Linux on Power 而更新了本文。访问 Linux on Power 社区 了解针对该 SDK 的更多链接,以及来自 Wainer dos Santos Moschetta 的有关该 SDK 的博客文章。
感谢 Roberto Guimaraes Dutra de Oliveira/Brazil/IBM@IBMBR 和 Leonardo Rangel Augusto/Brazil/IBM@IBMBR 更新 IBM SDK for PowerLinux 中的 Migration Advisor。
参考资料
- 加入 Linux on Power 社区:这是您的一站式技术中心,提供了您需要和想要知道的有关移植到 Linux on Power 上和在 Linux on Power 上进行开发所需的所有知识。
- 加入讨论论坛。
- 了解您的的应用程序是否有资格使用 Chiphopper 产品。
- 有关 Linux on IBM Power Systems 的更多信息
相关信息
- POWER8 事实和特性
- 要帮助确定要使用哪个 Linux on Power 发行版,请访问 SUSE Linux 和 Red Hat 网站。
- 考虑迁移到 GNU Make 来构建您的应用程序。
推荐的工具和文档
- 有关基于 Eclipse 技术的 IBM SDK for Linux 的更多信息,请参阅 IBM 的 SDK for Linux on Power
- 要利用最新的 GCC 和 POWER 处理器调优的库,请访问 IBM SDK for PowerLinux
- 有关用于 Linux on Power 的 OProfile 的更多信息,请访问 OProfile 网站。
- 要了解链接后优化工具的更多信息(也称为 FDPRpro),请访问 链接后优化 社区。
- 有关 GCC 编译器的更多信息,请查阅 GCC 编译器手册。