从 Solaris 向 Linux on POWER 迁移指南

从 Solaris 向 Linux on POWER 迁移指南



  遵循这个分为六步的指南,可以加速移植速度。了解移植过程中通常会遇到的 Solaris 与 Linux™ on POWER™ 之间的差别。介绍在基于 IBM® POWER 处理器的系统上运行的 Linux 的开发环境,查看 SUN 的编译器/连接程序开关与 GNU GCC 和 IBM 原始编译器的开关之间的比较。最后,了解用于性能分析的工具和用于 Linux on POWER 的软件包。

  简介

  通常,将基于 Solaris 的应用程序移植到 Linux 是一项简单的任务,因为 Solaris 和 Linux 都是基于 UNIX® 的。要实现这个移植,通常只需在一些编译器和连接程序开关中通过较小的更改实现重新编译即可。只有当应用程序依靠于系统特定实现时,才需要对它们进行较大改动。虽然 Solaris 和 Linux 都设计为遵循标准,但是它们的实现中有时会出现差别。本指南将着重讲述这些差别,并在 Linux 端没有对等项时,提供可能的解决方案。

  本文包含的内容如下:

  建议的迁移步骤。

  字节排列顺序以及 32 位和 64 位移植问题。

  Linux on POWER 的开发环境。

  Solaris 与 Linux 之间的差别。

  可用于 Linux on POWER 的性能调优工具和软件包工具。

  关于本指南

  本指南将重点介绍从运行于 32 位或 64 位 SPARC、Intel 或 AMD x86 架构之上的 Solaris 到运行于 POWER 架构之上的 Linux 的迁移。对于 Solaris,我们的讨论将基于版本 9 及其更高版本。对于 Linux,我们将重点讨论基于 IBM POWER 处理器的服务器中的可用发行版本:SUSE LINUX Enterprise Server 9 和 Red Hat Enterprise Linux AS V3 update 4。

  移植路标

  下列步骤提供了一个成功迁移的路标:

  步骤 1:准备

  通过研究来了解源平台与目标平台之间的差别。例如,需要知道源平台上的字节排列顺序是否与目标平台上的字节排列顺序有所不同。如果源平台是 Solaris/x86,则您应该考虑字节排列顺序,因为 Linux on POWER 是 big-endian 的(注:字节顺序是 0x1234),而 Solaris/x86 是 little-endian 的(注:字节顺序是 0x3412)。

  还要确定目标平台上是否包含所有必需的第三方包(如数据库和类库)。对于 32 位应用程序,要考虑是否有必要迁移到 64 位。在下面的 一般移植注释 小节中,将提供字节排列顺序以及 32 位到 64 位问题的信息。

  还要决定哪个编译器用于目标平台。如果应用程序是性能敏感的,那么还要考虑使用原始编译器。在 Linux on POWER 的开发环境 小节中,将提供关于 Linux on POWER 的可用编译器的详细信息。

  步骤 2:配置

  这一步中包括设置开发环境、设置环境变量、对 makefile 进行更改,等等。在结束这个阶段时,应该可以开始构建应用程序了。

  步骤 3:构建

  这一步中包括修复编译器错误、连接程序错误和其他信息。代码经常要多次重复执行步骤 2 和步骤 3 才能生成没有问题的构建。

  步骤 4:运行时测试和调试

  成功构建应用程序后,还要对其进行测试,检查运行时错误。

  步骤 5:性能调优

  现在,移植的代码已经可以在目标平台上运行。可以通过监控性能来确保移植的代码按照预期的那样执行。如果没有按照预期的那样执行,必须进行性能调优。在本文的 性能调优 小节中,将提供关于可用于 Linux on POWER 的性能调优工具的详细信息。

  步骤 6:包装

  您是否要将得到的代码传送给其他人?如果要传送,您想使用哪种包装方法?Linux 提供了多种方法来包装应用程序,比如自安装外壳脚本或 RPM。软件包装 小节中将提供关于 Linux 的软件包装的详细信息。

  一般移植注释

  这一节将提供何时将应用程序从 32 位迁移到 64 位的信息,以及跨越字节排列顺序障碍的应用程序要考虑的一些问题。从 Solaris on x86 平台上迁移的应用程序将跨越字节排列顺序障碍。熟悉 32 位到 64 位迁移和字节顺序的开发人员可以跳到下一小节 Linux on POWER 的开发环境。

  32 位到 64 位

  一旦决定移植到 Linux on POWER,如果应用程序目前是 32 位的,那么还要考虑将应用程序移植到 64 位。不过,不需要 64 位功能的 32 位应用程序应该保持 32 位。涉及迁移到 64 位的任何移植工作都应该分为两步:首先,以 32 位形式迁移到目标平台,然后才能迁移到 64 位。否则,因为平台迁移导致的问题很容易与因为迁移到 64 位导致的问题混淆。也就是说,如果应用程序可以满足下列条件,则应该移植到 64 位:

  受益于大于 4GB 的虚拟地址空间。

  受益于更多的物理内存(大于 4GB),同时用户可能在拥有大于 4GB 的物理内存的系统中进行部署。

  受益于 64 位序列长度。

  受益于完整 64 位注册,以便进行有效的 64 位运算。

  使用大于 2GB 的文件。

  应用程序可以保持 32 位,同时仍旧在 64 位 Linux on POWER 内核中运行,而无需进行任何代码更改。基于 POWER 处理器的 IBM Linux 服务器支持 32 位和 64 位应用程序同时在 64 位架构中运行,而在这两种模式之间不会有任何性能下降。

  将 32 位应用程序转换为 64 位应用程序时,可能会出现两个基本问题:数据类型不一致和使用不同数据模型的应用程序之间的互操作。

  64 位环境使用与 32 位环境不同的数据类型模型。对于 Linux on POWER,ILP 32 模型用于 32 位环境中,而 LP64 用于 64 位环境中。这两种模型之间的区别在于长整型和指针的大小。因此,当应用程序从 ILP32 转换为 LP64 时,数据类型中会发生基本变化。长整型和整型的大小不再相同。指针和整型的大小也不再相同。如果对象(如 struct 和 union)包含指针或长整型数据类型,那么它们的大小在 64 位环境中会比较大。通常,由这些差别引起的其他问题有:

  数据截断

  指针赋值

  数据对齐

  数据共享

  将应用程序从 32 位迁移到 64 位环境的主题在其他文章中已经进行了大量讲述。因此本文中我们将不再详细说明如何处理这些移植问题。有关从 32 位到 64 位移植的详细信息,请参阅 IBM 红皮书“AIX 5L Porting Guide”的第 3 章。

  字节顺序(字节排列顺序)

  因为 SPARC 和 POWER 架构都是 big-endian 的,所以在这些平台之间移植应用程序时,不会存在字符排列顺序问题。然而,如果将 Solaris on x86 应用程序移植到 Linux on POWER,可能需要处理字节排列顺序可移植性问题,因为 x86 是 little-endian 架构。

  在将应用程序从一种架构类型迁移至另一种架构类型的过程中,经常会遇到字节排列顺序(endianness)问题。字节排列顺序是数据元素及其单个字节在内存中存储和表示时的顺序。有两类字节排列顺序:big-endian 和 little-endian。

  对于 big-endian 处理器,如 POWER、PowerPC™ 和 SPARC,在将字放在内存中时,是从最低位地址开始的,首先放入最重要的字节。另一方面,对于 little-endian 处理器,如 Intel 和 Alpha 处理器,首先放入的是最不重要的字节。

  下图显示了 big-endian 和 little-endian 处理器如何在其内存中放置十六进制值,如 0x89ABCDEF:

  图 1. 字节顺序(字节排列顺序)

从 Solaris 向 Linux on POWER 迁移指南

  出现字节排列顺序问题的原因之一是不一致的数据引用。它经常表现为由于数据元素转换、使用联合数据结构或使用和操作位域导致数据类型不匹配。

  下列源代码说明了因为使用某一指针进行不一致的数据引用而引起的字节排列顺序问题:

  清单 1. 不一致的数据引用

int main() {
    int value;
    char *p;
    p = (char*) &value;
   value = 0x89ABCDEF;
   printf(“%X.%X.%X.%Xn" p[0], p[1], p[2], p[3]);
return 0;
}

  Linux on POWER 的开发环境

  这一节将描述 Linux on POWER 开发环境的元素,您对此应该比较熟悉。

  Linux 发行版本

  有两个一流 Linux 供应商提供了 Linux for POWER 的企业版:SUSE LINUX 和 Red Hat。虽然 Linux 团体非常尊重这两个供应商,但他们的产品却有所不同。不过,对于大多数供应商,从同一代码基础跨以上任何一种产品来部署软件是没有问题的。熟悉 SLES9、RHEL3 和 Solaris 10 中特定级别开发环境的开发人员可以跳到下一小节 Solaris 和 Linux on POWER 之间的区别。

  SLES9 SUSE LINUX Enterprise Server, version 9 (SLES9) 基于 Linux 2.6 内核,支持 Linux on POWER 硬件中的所有企业功能。该开发环境提供了 GCC 3.3.3、glibc 2.3.3 和 GNU binutils 2.15.90。而在 Solaris 10 中,则可以使用 GCC 3.3.2 和 glibc 2.2.3。随 SLES9 同时提供的有 IBM JDK 1.4.2。与 SLES9 一起打包的还有其他许多开放源代码开发库,如 perl、python 和 gtk 等。因此,32 位和 64 位开发环境可以用于 C、C++ 和 Java™ 技术。 RHEL3 Red Hat Enterprise Linux, version 3, update 4 基于 Linux 2.4.21 内核,由于从 Linux 2.6 内核进行了大量移植,RHEL3 提供了 2.6 内核功能,如同步多线程(SMT)和 Native POSIX Thread Library(NPTL)。与 SLES9 一样,RHEL3 开发环境为 C、C++ 和 Java 技术的 32 位和 64 位开发提供了工具和通用开发源代码库。这是通过 GCC 3.2.3、glibc 2.3.2、GNU binutils 2.14.90 和 IBM JDK 1.4.2 来完成的。

  编译器

  与 Solaris 相似,除 GNU Compiler Collection 之外,Linux on POWER 还提供了高性能编译器集合 —— IBM XL C/C++ 编译器集合。XL C/C++ Advanced Edition for Linux Web 站点中有关于其许可证的详细信息。

  下面是每个编译器集合及其优点的简要概述:

  GCC 对于跨多个平台(其中 GCC 编译器为原始编译器)开发的项目,经常使用 GCC 编译器来部署 Linux on POWER 的应用程序。对于性能不是至关重要的应用程序尤其如此;例如,一些小的实用程序。GCC 还允许使用一些只有 GCC 才理解的代码原型,如 GCC 特定宏。然而,IBM XL C/C++ 编译器中合并了其中许多“GCC-isms”。

  XL C/C++ XL C/C++ V7.0 是 VisualAge V6.0 for Linux 的后续版本。XL C/C++ 提供了 GCC 的高性能备选方案以及其他许多功能。幸运的是,XL C/C++ 编译器生成 32 位和 64 位 GNU elf 对象,它们与 GCC 编译器生成的对象完全兼容,因为 XL C/C++ 编译器使用与 GCC 相同的 GNU 库、连接程序、汇编程序和 binutil。实际上,以前专门由 GCC 编译器提供的功能已经移植到 XL C/C++,以促进源代码兼容性;例如,一些 GCC 宏和在线功能。但是除了兼容性,XL C/C++ 还提供了无可比拟的性能。

  已经为 POWER 架构开发并修改了成熟的 XL C/C++ 优化例程并且进行了展示。通常对于高性能计算应用程序,尤其是那些高度依赖浮点操作的应用程序,使用 XL C/C++ 进行重新编译会很有好处。

  除了算法优化例程,XL C/C++ 还提供了一些 GCC 中还没有的架构特定支持 —— 尤其支持 IBM eServer® BladeCenter™ JS20 产品中起重要作用的 VMX 矢量指令。GCC 3.3(SLES9,而不是 GCC 的 RHEL3 版本)支持这些指令,但是只有 XL C/C++ 版本 7 提供对未矢量化代码进行自动矢量化的功能,并对这些代码进行优化,以便适用于 POWER VMX 指令。

  虽然 XL C/C++ for Linux on POWER 不是免费的,然而开发人员可以以优惠价格获得该产品,以及获得该产品的免费试用版。XL C/C++ Advanced Edition for Linux Web 站点中有相关的详细信息。

  Binutil 在 Solaris 10 中,可以选择使用原始构建实用程序或使用随 GNU 一起提供的实用程序。通过 Linux on POWER,GNU binutil 用于使用 XL C/C++ 编译器集合和 GCC 来生成对象。GNU Binutils 站点中非常详细地讲述了 GNU binutil。除了标准 binutil,开发人员还应该知道 elflint、nm、readelf、size 和 strip 的修改版本;这些是 RHEL3 中包装的 elfutil 工具。已经针对 Linux 特定功能对这些工具进行了修改,但是这些程序的普通 GNU binutil 版本仍然可用。

  Java 技术

  如果 Java Runtime Environment 与开发软件平台的 Java Developer Kit (JDK) 兼容,Java 技术允许开发人员跨多个平台进行部署,而不用重新编译代码。通过 IBM Developer Kit for Linux, Java Technology Edition version 1.4.2,RHEL3 和 SLES9 都支持 Linux on POWER Java 开发。这一套开发人员套件有 32 位版本和 64 位版本,IBM 没有对该套件收取费用。IBM 开发人员套件还与 RHEL3 和 SLES9 media 一起打包,其更新受到发行版维护包的支持。

  64 位 Java 和 NPTL RHEL3 中的线程化 32 位和 64 位开发可以通过 NPTL 或更早的 Linux Threads 库来实现。然而,对于 Java,只有 NPTL 64 位线程库是受支持的。对于 Linux on POWER,IBM JDK 1.4.2 的 64 位版本不支持 LinuxThreads。

  Solaris 和 Linux on POWER 之间的差别

  在这一节中,将了解 Solaris 与 Linux on POWER 之间的差别,其中包括系统调用、信号、数据类型、makefile、编译器选项、连接程序选项和线程库等。

  系统调用和库函数

  系统调用是用户程序使用的接口,它可以使内核代表调用线程或进程执行特定函数。要执行系统调用,还需要授权内核模式的上下文切换。库函数是普通 C 函数。它们没有任何上下文切换资源损耗,因为它们是使用其每个进程的地址空间的一部分。一些 Solaris 系统调用和库函数可能不能用于 Linux 中。发生这种情况时,可能要在 Linux 中实现包装器调用。

  Linux on POWER 目前提供了 256 种不同的系统调用。/usr/include/asm-ppc/unistd.h 或 /usr/include/asm-ppc64/unistd.h 中有系统调用的列表。

  下面是 Solaris 和 Linux 之间不兼容的一些例子:

  regexp() 和 regcmp():Solaris 中的例程 regexp() 和 regcmp() 在 Linux 上由 regexp() 和 regcmp() 替代。

  Solaris 中的 Dirent 结构与 Linux 中的不同:

  清单 2. Linux 中的 Dirent 结构

struct dirent
    {
#ifndef __USE_FILE_OFFSET64
    __ino_t d_ino;
    __off_t d_off;
#else
   __ino64_t d_ino;
   __off64_t d_off;
#endif
    unsigned short int d_reclen;
    unsigned char d_type;
    char d_name[256];
};

  清单 3. Solaris 中的 Dirent 结构

typedef struct dirent {
     ino_t d_ino; /* "inode number" of entry */
     off_t d_off; /* offset of disk directory entry */
     unsigned short d_reclen; /* length of this record */
     char d_name[1]; /* name of file */
} dirent_t;

  文件系统接口例程:Solaris 文件系统例程使用 vfstab 结构,函数名中包含 vfs,如 getvfsent。Linux 提供等同的接口,但是例程使用 fstab 结构,例程名称中包含 fs,如 getfsent。Solaris 中的 vfstab 结构是在 /usr/include/sys/vfstab.h 中定义的。Linux 中的 fstab 则是在 /usr/include/fstab.h 中定义的。

  Device Information Library Functions (libdevinfo):libdevinfo 库包含一组接口,用于访问设备配置数据,如主号码和辅助号码。标准 Linux 安装不支持这一点。

  CPU Affinity:在默认情况下,多处理器系统中的进程在多个 CPU 之间跳来跳去。在某些情况下,通过明确地将进程绑定到特定 CPU(分配 CPU 亲和力)可以提高性能。Solaris 中 CPU 亲和力的系统调用与 Linux 中的不同。Linux 2.6 内核为 CPU 亲和力提供了 sched_setaffinity() 和 sched_getaffinity()。有关的详细信息,请参阅 Linux 手册。

  下表列出了 Solaris 系统调用,这些系统调用与 Linux 中系统调用的名称、签名不同,或不能用于 Linux 中:

  表 1. Solaris 系统调用

SolarisLinux注释
acl、faclN/A获取或设置文件的 Access Control List (ACL)
adjtimeN/A更正时间,以允许系统时钟同步
auditN/A向审核日志写入记录
auditonN/A对审核进行操作
auditsvcN/A向特定文件描述符写入审核日志
getacct、putacct、wracctN/A获得、放入或写入扩展报告数据
getaudit、setaudit、getaudit_addr、setaudit_addrN/A获取和设置进程审核信息
getauid、setauidN/A获取和设置用户审核身份
getdentsgetdents**读取目录条目,并以单独格式将其放入文件系统中。Linux 中的 Dirent 结构与 Solaris 中的不同
getmsg、getpmsgN/A获取数据流中的下一条消息
getpflags、setpflagsN/A获取或设置进程标志
getppriv、setpprivN/A获取或设置权限集合
getustack、setustackN/A检索或更改 per-LWP 堆栈边界信息
issetugidN/A确定当前可执行程序是在运行 setuid 还是在运行 setgid
llseek_llseek移动扩展读/写文件指针
_lwp_cond_signal、_lwp_cond_broadcastN/A对条件变量发送信号
_lwp_cond_wait、_lwp_cond_timedwait、_lwp_cond_reltimedwaidN/A等待条件变量
_lwp_infoN/A返回单个 LWP 的时间核算信息
_lwp_killN/A向 LWP 发送信号
_lwp_mutex_lock、_lwp_mutex_unlock、_lwp_mutex_trylockN/A互斥
_lwp_selfN/A获取 LWP 标识符
_lwp_sema_wait、_lwp_sema_trywait、_lwp_sema_init、_lwp_sema_postN/A信号灯操作
_lwp_suspend、_lwp_continueN/A继续或暂停 LWP 执行
memcntlN/A内存管理控制
meminfoN/A提供内存信息
mountmount*装载文件系统。Solaris 中此系统调用的签名与 Linux 的不同
msgidsN/A发现所有消息队列标识符
msgrcvmsgrcv*消息接收操作。Linux 在其一个参数中使用 struct msgbuf 类型
msgsnapN/A消息队列快照操作
msgsndmsgsnd*消息发送操作。Linux 在其一个参数中使用 struct msgbuf 类型
ntp_adjtimeN/A调整本地时钟参数
ntp_gettimeN/A获取本地时钟值
open、openatopen*打开文件。openat() 不能在 Linux 中使用
pcsampleN/A程序执行时间配置文件
p_onlineN/A返回或更改处理器操作状态
priocntlN/A进程调度器控制
priocntlsetN/A一般化的进程调度器控制
processor_bindsched_setaffinity将 LWP 绑定到处理器
processor_infoN/A确定处理器类型和状态
pset_bindN/A将 LWP 绑定到处理器集合
pset_create、pset_destroy、pset_assignN/A管理处理器集合
pset_infoN/A获取处理器集合的信息
pset_listN/A获取处理器集合的列表
pset_setattr, pset_getattrN/A设置或获取处理器集合属性
putmsg、putpmsgN/A发送流消息
rename、renameatrename*更改文件名。Linux 没有 renameat() 函数
resolvepathN/A分析路径名称的所有符号链接
semidsN/A发现所有信号灯标识符
setrctl、getrctlN/A设置或获取资源控制值
settaskid、gettaskid、getprojidN/A设置或获取任务或项目 ID
shmidsN/A发现所有共享内存标识符
sigsend、sigsendsetN/A向处理器或处理器组发送信号
__sparc_utrap_installN/A安装 SPARC V9 用户陷阱处理程序
fstatatN/A获取文件状态
swapctlN/A管理交换空间
uadminN/A管理控制
unlink、unlinkatunlink*删除目录条目。Linux 没有 unlinkat() 函数
futimesatN/A设置文件访问权和修改时间。它分析关于 fildes 参数的路径
waitidN/A等待子进程来更改状态
yieldsched_yield放弃对其他轻量级进程的执行

  信号

  信号用于向进程或线程通知特定事件。Linux 支持 POSIX 标准信号和 POSIX 实时信号。每个信号都有惟一名称和相应的信号编号。例如,对于 Solaris,SIGSTOP 的信号编号是 23,但是对于 Linux on POWER,其信号编号则为 19。/usr/include/bits/signum 中可以找到 Linux 上的信号编号定义。

  对于所有可能的信号,系统会定义出现信号时执行的默认操作:

  Terminate: 默认操作是终止进程。 Ignore: 默认操作是忽略信号。 Core: 默认操作是终止进程并转储内核。 Stop: 默认操作是停止进程。

  有关 Linux 信号的完整列表,包括每个信号的简短描述及发出信号时的默认行为,请通过调用下列命令查看 Section 7 中的信号手册:# man 7 signal

  对于 Linux,请注意以下事项:

  SIGABRT 和 SIGIOT 是相同的。

  SIGCLD 和 SIGCHLD 是相同的。

  SIGPOLL 和 SIGIO 是相同的。

  对于 Linux,SIGPWR 的默认操作是终止进程,而在 Solaris 中则忽略该操作。

  Solaris 支持下列信号,但 Linux on POWER 不支持:

  表 2. Solaris 支持的信号

名称默认操作描述
SIGEMTcore模拟陷阱
SIGWAITINGignore线程库使用的并发信号
SIGLWPignore线程库使用的 Inter-LWP 信号
SIGFREEZEignore检查点暂停
SIGTHAWignore检查点继续
SIGCANCELignore线程库使用的取消信号
SIGLOSTignore资源丢失(运行于 Sparc 上的 Linux 支持该项)
SIGXRESignore资源控制超出

  在 Soalris 和 Linux 上,sigset_t 的定义有所不同。

  在 Linux 中,sigset_t 在 /usr/include/bits/sigset.h 中定义为:# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long
int)))
typedef struct
  {
    unsigned long int __val[_SIGSET_NWORDS];
  } __sigset_t;
#endif

  在 Solaris 中,sigset_t 在 /usr/include/sys/signal.h 中定义为:typedef struct {                   /* signal set type */
       unsigned int    __sigbits[4];
} sigset_t;

  基本数据类型和对齐

  系统中可以有两种不同的数据类型:基本数据类型和衍生数据类型。

  基本数据类型是 C 和 C++ 语言规范定义的所有数据类型。下表对 Linux on POWER 和 Solaris 中的基本数据类型进行了比较:

  表 3. 基本数据类型

 Linux on POWERSolaris
基本类型ILP32LP64ILP32LP64
char8888
short16161616
init32323232
float32323232
long32643264
pointer32643264
long long64646464
double64646464
long double64/128*64/128*128128

  *在 Linux on POWER 中,long double 的默认大小是 64 位。如果 XL C/C++ 编译器中使用了编译器选项 –qlongdouble ,那么其大小可以增加到 128 位。

  当在平台之间或在 32 位和 64 位模式之间移植应用程序时,需要考虑不同环境中可用对齐设置之间的差别,以避免可能发生的性能下降和数据破坏。对于 IBM XL C/C++ 编译器,集合(C/C++ 结构/联合及 C++ 类)内的每个数据类型将根据 linuxppc 或 bit-packed 规则沿字节边界对齐,其中 linuxppc 为默认值。linuxppc 与默认 GCC 对齐规则是兼容的。

  下表用字节显示了 linuxppc 和 bit-packed 的对齐值:

  表 4. 对齐值

数据类型linuxppcbit-packed
bool11
char11
wchar_t (32-bit)21
wchar_t (64-bit)41
intl41
short21
long (32-bit)41
long (64-bit)81
long long81
float41
double81
long double81

  系统衍生数据类型

  衍生数据类型是现有基本类型或其他衍生类型的衍生物或结构。根据使用的数据模型(32 位或 64 位)和硬件平台,系统衍生数据类型可以具有不同的字节大小。下表显示了 Linux 中的一些衍生数据类型,它们与 Solaris 中的那些衍生数据类型不同:

  表 5. 衍生数据类型

OSgid_tmode_tpid_tuid_twint_t
Solaris ILP32 llongunsigned longlonglonglong
Solaris LP64intunsigned intintintint
Linux ILP32unsigned intunsigned intintunsigned intunsigned int
Linux ILP64unsigned intunsigned intintunsigned intunsigned int

  GNU Make 与 Solaris Make

  如果在源平台中使用 Solaris Make,为了在 Linux 中使用 GNU Make,需要修改 makefile。

  要获得 Solaris Make 和 GNU Make 之间差别的详细信息,请参阅 IBM 红皮书“AIX 5L Porting Guide”的第 6 章。

  编译器和连接程序选项

  如前所述,对于 Linux on POWER,可以使用两种编译器:GNU GCC 和 IBM XL C/C++ V7.0。下面是 SUN Studio C/C++ 编译器广泛使用的编译器选项的列表,以及 GNU GCC 和 IBM XL C/C++ 编译器的等同选项:

  表 6. 编译器选项

SUN Studio 选项GNU GCC 选项XL C/C++ 选项描述
-#-v-v指示编译器报告编译进度信息
-###-###-#跟踪编译,而不调用任何东西
-Bstatic-static-qstaticlink让连接编辑程序仅查找名为 libx.a 的文件
-Bdynamic(Default)(Default)指示连接编辑程序在给定了 -lx 选项时查找文件名 libx.so,然后查找 libx.a
-G-shared-qmkshrobj生成共享对象,而不是生成动态链接可执行程序
-xmemalign-malign-natural、

  -malign-power

-qalign指定最大假定内存对齐(memory alignment),以及未对齐数据访问的行为
-xO1、-xO2、-xO3、-xO4、-xO5-O、-O2、-O3-O、-O2、-O3、-O4、-O5编译过程中在所有级别上优化代码
-KPIC-fPIC-qpic=large生成位置无关代码以用于共享库中(大模型)
-Kpic-fpic-qpic=small生成位置无关代码以用于共享库中(小模型)
-mt-pthreadUse _r invocation mode编译和链接多线程代码
-R dirlist-Wl、-rpath、dirlist-Wl、-rpath、dirlist构建可执行文件的动态库搜索路径
-xarch-mcpu、-march-qarch、-qtune指定目标架构指令集合

  Solaris 线程和 NPTL

  这一节将说明开发人员将多线程应用程序从 Solaris 迁移到 Linux 时会遇到的问题。

  Solaris 支持两种线程实现:Solaris 线程和 POSIX 线程(pthreads)。有一些函数由 Solaris 线程 API 实现,而不是由 pthreads API 实现,有一些则相反。对于那些符合以上情况的函数,关联的参数可能不符合。下列是专门由 Solaris 线程 API 支持的功能:

  可以创建端口监控程序。端口监控程序不影响进程退出状态。进程可以通过调用 exit() 退出,也可以通过让进程中的每个非端口监控程序调用 thr_exit() 来退出。

  可以使用 thr_suspend() 和 thr_continue() 来暂停或继续线程的执行。注意 thr_suspend() 可以暂停目标线程,而不涉及线程可能拥有的锁。如果暂停线程调用某一个函数,而该函数需要已暂停的目标线程所拥有的锁,那么就会出现死锁。

  可以允许线程等待进程中任何未检验出的线程终止。当 thr_join() 的第一个参数设为 0 时就会这样。如果将 pthread_join() 的第一个参数设为 0,程序将因段错误而终止。

  下表将关键 Solaris 线程函数与 pthreads 函数进行了比较。对于其他 Solaris 线程函数,请参阅 SUN 的 Solaris Threading Guide。

  表 7. 线程和 pthreads 函数

Solaris 线程 APILinux POSIX 线程 API描述
thr_create()pthread_create()创建新的控制线程
thr_exit()pthread_exit()终止执行调用线程
thr_join()pthread_join()暂停调用线程,直到目标线程完成
thr_kill()pthread_kill()向其他线程发送信号
thr_self()pthread_self()返回调用进程的线程 ID
thr_yield()sched_yield()用其他线程替换当前线程
thr_getprio()pthread_getschedparam()检索线程的优先级参数
thr_setprio()pthread_setschedparam()修改线程的优先级参数
thr_getspecific()pthread_getspecific()将新的线程特定值绑定到特定键
thr_setspecific()pthread_setspecific()将新的线程特定值绑定到特定键
thr_getconcurrency()pthread_getconcurrency()获取线程并发级别
thr_setconcurrency()pthread_setconcurrency()设置线程并发级别
thr_sigsetmask()pthread_sigmask()更改或检查调用线程的信号掩码
thr_keycreate()pthread_key_create()创建确定线程特定数据的位置的键
N/Apthread_key_delete()删除确定线程特定数据的位置的键
thr_suspend()N/A暂停执行指定的线程
thr_continue()N/A继续执行暂停的线程
fork1()fork()常规分支
forkall()N/A复制所有分支

  在 Solaris 9 及其更早的版本中,fork() 的行为与 POSIX 线程中 fork() 的行为不同。在 POSIX 线程中,fork() 创建新的进程,复制子进程中的全部地址空间。不过,它只复制子进程中的调用线程。Solaris 线程 API 还提供复制所有分支语义的 forkall()。该函数复制子进程中的地址空间和所有线程。POSIX 线程标准不支持这个特性。

  有一些 POSIX 线程例程在 Solaris 中可以实现,但在 Linux 中却无法实现,有一些则相反。下表列出了这些例程:

  表 8. POSIX 线程例程

例程SolarisLinux
pthread_cond_reltimedwait_npyn
pthread_mutexattr_getprioceilingyn
pthread_mutexattr_getprotocolyn
pthread_mutexattr_getrobust_npyn
pthread_mutexattr_setprioceilingyn
pthread_mutexattr_setprotocolyn
pthread_mutexattr_setrobust_npyn
pthread_mutex_consistent_npyn
pthread_mutex_getprioceilingyn
pthread_mutex_reltimedlock_npyn
pthread_mutex_setprioceilingyn
pthread_rwlock_reltimedrdlock_npyn
pthread_rwlock_reltimedwrlock_npyn
pthread_setschedprioyn
pthread_attr_getaffinity_npny
pthread_attr_setaffinity_npny
pthread_cleanup_pop_restore_npny
pthread_cleanup_push_defer_npny
pthread_getattr_npny
pthread_kill_other_threads_npny
pthread_rwlockattr_getkind_npny
pthread_rwlockattr_setkind_npny
pthread_timedjoin_npny
pthread_tryjoin_npny

  2.6 版之前发行的所有 Linux 版本中的 Linux 线程库都称为 LinuxThreads。该库自 glibc 2.0 以来已经得到 GNU C 库支持,而且在很大程度上与 POSIX 是兼容的。从 2.6 内核开始,引入了新的经过改善的线程库,称为 Native POSIX Threading Library(NPTL)。该实现在 LinuxThreads 之上提供了显著的性能提高。NPTL 与 POSIX 规范的兼容性也强于 LinuxThreads 包与 POSIX 规范的兼容性。然而,只使用 2.6 内核并不意味着就使用了 NPTL。发出下列命令可以查看正在使用的 POSIX 实现:

$ getconf GNU_LIBPTHREAD_VERSION

  NPTL 实现一对一线程模型,在该模型中,用户线程与内核线程之间存在一对一的关系。NPTL 还实现进程间 POSIX 同步原语。特别是线程选项。

  现在支持 PTHREAD_PROCESS_SHARED。在默认情况下,创建每个线程时,detachstate 属性被设为 PTHREAD_CREATE_JOINABLE,调用策略设为 SCHED_OTHER,而且没有用户提供的堆栈。

  如果 Solaris 应用程序使用 POSIX 线程 API,那么将其移植到 Linux 会非常简单。注意,即使使用 GCC,Solaris 也不支持 NPTL。下表显示了 Linux 中 POSIX 线程属性的默认值:

  表 9. Linux 中 POSIX 线程属性的默认值

属性默认值
scopePTHREAD_SCOPE_SYSTEM
detachstatePTHREAD_CREATE_JOINABLE
schedparam0
inhiritschedPTHREAD_EXPLICIT_SCHED
schedpolicySCHED_OTHER

  虽然许多应用程序将从 2.4 内核迁移到 2.6,而无需重新编译,但是增加 NPTL 可能需要在多线程应用程序中进行少量的修改。关于如何通过使用 LinuxThreads 将应用程序迁移到 NPTL 不在本文的范围之内。

  性能调优

  一旦在 Linux 中移植并成功执行了代码,需要完成性能监控和性能调优,以确保移植的代码在目标平台上可以正常执行。这一节将提供 Linux on POWER 中可以使用的工具列表,帮助您完成以上操作。

  Performance Inspector

  这个工具套件可以用于标识应用程序(C/C++/Java)中的性能问题,并显示应用程序如何与 Linux 内核交互。它包含 7 个工具:

  TProf 是一个计时器剖析器,标识在用户指定的时间间隔内哪些代码在 CPU 上运行。它用于报告应用程序及内核中的热点。它主要记录每次系统时钟中断时(每个 CPU 每秒发生 100 次中断)正在运行的代码。

  PTT 收集每个进程的统计数据,如 CPU 周期数目、中断数目和发出线程的次数。

  AI 显示在用户指定的时间间隔内的 CPU 使用率统计数据。

  JLM 提供 Java 2 技术中基于锁定的统计数据。

  JProf 是共享库,与 Java jvmpi 接口连接。

  POST 基于其他工具的输出生成报告。

  A2N 由 POST 使用,用于将代码执行映射到正在跟踪的应用程序。

  在撰写本文时,Performance Inspector 支持 SUSE LINUX Enterprise Server 9 (2.6.5-7.97) 和 Red Hat Enterprise Linux V3.0 Update 2。

  Post-Link optimization

  在将程序用于一些典型的工作负载时,该工具通过收集关于程序行为的信息来优化程序的可执行映像。然后将重新分析该程序(与收集的配置文件一起),应用全局优化(包括程序重构),并创建用来优化该工作负载的新的程序版本。优化程序生成的新程序通常运行得更快,使用的实际内存也比原始程序少。

  在撰写本文时,下列 Linux 发行版中支持 Post-Link Optimization 工具:SUSE LINUX Enterprise Server 8 及其更高版本、Red Hat Enterprise Linux 3。

  Oprofile

  Oprofile 基于硬件相关事件(如缓存遗漏或 CPU 周期)提供代码的配置文件。例如,Oprofile 可以帮助确定是哪个源例程导致大部分缓存遗漏。Oprofile 使用包括 IBM POWER4™、POWER5™ 和 PowerPC™ 970 在内的许多 CPU 中提供的硬件性能计数器。有关的详细信息,请参阅 Oprofile Web 站点。

  用于 Linux on POWER 的 Oprofile 可以在 SUSE LINUX SDK 9 中使用。

  已安装系统的 /usr/share/doc/packages/oprofile 中有关于 Oprofile 的快速参考。

  软件包

  应用程序软件是放在称为包的管理单位中发送给最终用户的。包是相关文件(二进制文件、库、文档和源代码)和元数据的集合。包管理系统使用元数据来协调包中的所有块。Solaris 使用 pkgadmin 作为其包管理器。RPM 是 Linux 上广泛使用的包管理系统。有关 RPM 的进一步信息,请参阅 www.rpm.org。

  注意,Solaris 中 pkgadmin 使用的包规范模板文件的格式与 RPM 使用的 spec 文件的格式不同,将包信息从模板文件转换到 spec 文件中需要做大量工作。

  结束语

  大多数情况下,从 Solaris 到 Linux on POWER 的移植工作仅涉及在编译器/连接程序开关中进行重新编译或少量更改。然而,Solaris 和 Linux 的设计却根本不同。Solaris 侧重于性能、可伸缩性和可靠性,而忽略可移植性。另一方面,Linux 设计时考虑了可移植性,几乎今天可以使用的所有硬件平台都支持 Linux。不过,Linux 2.6 内核自 2.4 内核开始已经大大改善了性能、可伸缩性和可靠性。因此,一些可以在 Solaris 中使用的系统特定功能在 Linux 中无法使用。


本 文地 址 :  http://www.fengfly.com/plus/view-32067-1.html 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值