从Solaris向LinuxonPOWER迁移指南
ChinaUnix 发表于:12年08月17日 14:00 [转载] ChinaUnix
表 1 列出了 Solaris 系统调用,这些系统调用与 Linux 中系统调用的名称、签名不同,或在 Linux 中不可用:
表 1. Solaris 系统调用
Solaris | Linux | 说明 |
---|---|---|
acl、facl | N/A | 获取或设置文件的 Access Control List (ACL) |
adjtime | N/A | 更正时间,以允许系统时钟同步 |
audit | N/A | 向审计日志写入记录 |
auditon | N/A | 对审计进行操作 |
auditsvc | N/A | 向特定文件描述符写入审计日志 |
getacct、putacct、wracct | N/A | 获取、放入或写入扩展报告数据 |
getaudit、setaudit、getaudit_addr、setaudit_addr | N/A | 获取和设置进程审计信息 |
getauid、setauid | N/A | 获取和设置用户审计身份 |
getdents | getdents** | 读取目录条目,并以单独格式将其放入文件系统中。Linux 中的 Dirent 结构与 Solaris 中的不同 |
getmsg、getpmsg | N/A | 获取数据流中的下一条消息 |
getpflags、setpflags | N/A | 获取或设置流程标记 |
getppriv、setppriv | N/A | 获取或设置进程标志 |
getustack、setustack | N/A | 检索或更改 per-LWP 堆栈边界信息 |
issetugid | N/A | 确定当前可执行程序是在运行 setuid 还是在运行 setgid |
llseek | _llseek | 移动扩展读/写文件指针 |
_lwp_cond_signal、_lwp_cond_broadcast | N/A | 对条件变量发送信号 |
_lwp_cond_wait、_lwp_cond_timedwait、_lwp_cond_reltimedwaid | N/A | 等待条件变量 |
_lwp_info | N/A | 返回单个 LWP 的时间核算信息 |
_lwp_kill | N/A | 向 LWP 发送信号 |
_lwp_mutex_lock、_lwp_mutex_unlock、_lwp_mutex_trylock | N/A | 互斥 |
_lwp_self | N/A | 获取 LWP 标识符 |
_lwp_sema_wait、_lwp_sema_trywait、_lwp_sema_init、_lwp_sema_post | N/A | 信号灯操作 |
_lwp_suspend、_lwp_continue | N/A | 继续或暂停 LWP 执行 |
memcntl | N/A | 内存管理控制 |
meminfo | N/A | 提供内存信息 |
mount | mount* | 装载文件系统。Solaris 中此系统调用的签名与 Linux 的不同 |
msgids | N/A | 发现所有消息队列标识符 |
msgrcv | msgrcv* | 消息接收操作。Linux 在其一个参数中使用 struct msgbuf 类型 |
msgsnap | N/A | 消息队列快照操作 |
msgsnd | msgsnd* | 消息发送操作。Linux 在其一个参数中使用 struct msgbuf 类型 |
ntp_adjtime | N/A | 调整本地时钟参数 |
ntp_gettime | N/A | 获取本地时钟值 |
open、openat | open* | 打开文件。openat() 不能在 Linux 中使用 |
pcsample | N/A | 程序执行时间配置文件 |
p_online | N/A | 返回或更改处理器操作状态 |
priocntl | N/A | 进程调度器控制 |
priocntlset | N/A | 一般化的进程调度器控制 |
processor_bind | sched_setaffinity | 将 LWP 绑定到处理器 |
processor_info | N/A | 确定处理器类型和状态 |
pset_bind | N/A | 将 LWP 绑定到处理器集合 |
pset_create、pset_destroy、pset_assign | N/A | 管理处理器集合 |
pset_info | N/A | 获取处理器集合的信息 |
pset_list | N/A | 获取处理器集合的列表 |
pset_setattr、pset_getattr | N/A | 设置或获取处理器集合属性 |
putmsg、putpmsg | N/A | 发送流消息 |
rename、renameat | rename* | 更改文件名。Linux 没有 renameat() 函数 |
resolvepath | N/A | 分析路径名称的所有符号链接 |
semids | N/A | 发现所有信号灯标识符 |
setrctl、getrctl | N/A | 设置或获取资源控制值 |
settaskid、gettaskid、getprojid | N/A | 设置或获取任务或项目 ID |
shmids | N/A | 发现所有共享内存标识符 |
sigsend、sigsendset | N/A | 向处理器或处理器组发送信号 |
__sparc_utrap_install | N/A | 安装 SPARC V9 用户陷阱处理程序 |
fstatat | N/A | 获取文件状态 |
swapctl | N/A | 管理交换空间 |
uadmin | N/A | 管理控制 |
unlink、unlinkat | unlink* | 删除目录条目。Linux 没有 unlinkat() 函数 |
futimesat | N/A | 设置文件访问权和修改时间。它分析关于 fildes 参数的路径 |
waitid | N/A | 等待子进程来更改状态 |
yield | sched_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 中则忽略该操作。
表 2 显示了 Solaris 支持但 Linux on POWER 不支持的信号:
表 2. Solaris 支持的信号
名称 | 默认操作 | 说明 |
---|---|---|
SIGEMT | core | 模拟陷阱 |
SIGWAITING | ignore | 线程库使用的并发信号 |
SIGLWP | ignore | 线程库使用的 Inter-LWP 信号 |
SIGFREEZE | ignore | 检查点暂停 |
SIGTHAW | ignore | 检查点继续 |
SIGCANCEL | ignore | 线程库使用的取消信号 |
SIGLOST | ignore | 资源丢失(运行于 Sparc 上的 Linux 支持该项) |
SIGXRES | ignore | 资源控制超出 |
在 Soalris 和 Linux 上,sigset_t 的定义有所不同。在 Linux 上,它在 /usr/include/bits/sigset.h 中定义为:
清单 2. 如何在 Linux 上定义 sigset_t
# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) typedef struct { unsigned long int __val[_SIGSET_NWORDS]; } __sigset_t; #endif |
在 Solaris 上,它在 /usr/include/sys/signal.h 中定义为:
清单 3. 如何在 Solaris 上定义 sigset_t
typedef struct { /* signal set type */ unsigned int __sigbits[4]; } sigset_t; |
基本数据类型和对齐
系统中可以有两种不同的数据类型:基本数据类型 和衍生数据类型。
基本数据类型是 C 和 C++ 语言规范定义的所有数据类型。表 3 对 Linux on POWER 和 Solaris 中的基本数据类型进行了比较(值以字节为单位):
表 3. 基本数据类型
基本类型 | Linux on POWER, ILP32 | Linux on POWER, LP64 | Solaris, ILP32 | Solaris, LP64 |
---|---|---|---|---|
_Bool,bool | 1 | 1 | 1 | 1 |
char | 1 | 1 | 1 | 1 |
wchar_t | 2 | 4 | 4 | 4 |
short | 2 | 2 | 2 | 2 |
int | 4 | 4 | 4 | 4 |
float | 4 | 4 | 4 | 4 |
long | 4 | 8 | 4 | 8 |
pointer | 4 | 8 | 4 | 8 |
long long | 8 | 8 | 8 | 8 |
double | 8 | 8 | 8 | 8 |
long double | 16* | 16* | 16 | 16 |
*从 Red Hat Enterprise Linux 5 (RHEL5) 和 SUSE Linux Enterprise Server 10 (SLES10) 开始,在 Linux on POWER 中 long double 的默认大小是 128 位。在 RHEL5 或 SLES10 之前,如果 XL 编译器中使用了编译器选项 -qldbl128,那么其大小可以增加到 128 位。
当在平台之间或在 32 位和 64 位模式之间移植应用程序时,需要考虑不同环境中可用对齐设置之间的差别,以避免可能发生的性能下降和数据损坏。对于 IBM XL C/C++ 编译器,集合(C/C++ 结构/联合及 C++ 类)内的每个数据类型将根据 linuxppc 或 bit-packed 规则沿字节边界对齐(使用 -qalign 选项),其中 linuxppc 为默认值。linuxppc 使用 GNU C/C++ 对齐规则维护与 GNU C/C++ 对象的二进制兼容性。
表 4 用显示了 linuxppc 和 bit-packed 规则的对齐值:
表 4. 对齐值
数据类型 | linuxppc | bit-packed |
---|---|---|
_Bool,bool | 1 | 1 |
char | 1 | 1 |
wchar_t (32-bit) | 2 | 1 |
wchar_t (64-bit) | 4 | 1 |
int | 4 | 1 |
short | 2 | 1 |
long (32-bit) | 4 | 1 |
long (64-bit) | 8 | 1 |
long long | 8 | 1 |
float | 4 | 1 |
double | 8 | 1 |
long double | 16 | 1 |
SPARC 平台通常要求数据对齐遵循相等的数据大小:2 字节类型必须在 2 字节区域内,4 字节类型必须在 4 字节区域内。因此,当您从 Solaris 移植到 Linux 时,必须特别注意 Solaris 和 Linux 之间的不同大小的数据类型。
系统衍生数据类型
衍生数据类型是现有基本类型或其他衍生类型的衍生物或结构。根据使用的数据模型(32 位或 64 位)和硬件平台,系统衍生数据类型可以具有不同的字节大小。表 5 显示了 Linux 中的一些衍生数据类型,它们与 Solaris 中的那些衍生数据类型不同:
表 5. 衍生数据类型
数据类型 | Solaris ILP32 | Solaris LP64 | Linux on POWER ILP32 | Linux on POWER LP64 |
---|---|---|---|---|
gid_t | long | int | unsigned int | unsigned int |
mode_t | unsigned long | unsigned int | unsigned int | unsigned int |
pid_t | long | int | int | int |
uid_t | long | int | unsigned int | unsigned int |
wint_t | long | int | unsigned int | unsigned int |
GNU Make 与 Solaris Make
如果在源平台中使用 Solaris Make,为了在 Linux 中使用 GNU Make,需要修改 makefile。要获得 Solaris Make 和 GNU Make 之间差别的详细信息,请参阅 IBM 红皮书 “AIX 5L Porting Guide” 的第 6 章。
编译器选项
表 6 列出了 SUN Studio C/C++ 编译器广泛使用的编译器选项,以及 GNU Compiler Collection 和 IBM XL 编译器的等同选项:
表 6. 编译器选项
Sun Studio 选项 | GNU GCC 选项 | XL C/C++ 选项 | 说明 |
---|---|---|---|
-# | -v | -v | 指示编译器报告编译进度信息 |
-### | -### | -# | 跟踪编译,而不调用任何东西 |
-dn | -static | -qstaticlink | 指定静态链接 |
-dy | (Default) | (Default) | 指定动态链接 |
-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 | -pthread | Use _r invocation mode | 编译和链接多线程代码 |
-R dirlist | -Wl、-rpath、dirlist | -Wl、-rpath、dirlist | 构建可执行文件的动态库搜索路径 |
-xarch | -mcpu、-march | -qarch、-qtune | 指定目标架构指令集合 |
XL C/C++ 编译器支持 GNU 编译器选项的一部分,从而有利于移植使用 GCC 和 g++ 编译器开发的应用程序。将 gxlc 或 gxlc++ 调用命令与 GNU 编译器选项一起使用来启用该支持。这些调用命令使用纯文本配置文件来控制 GNU-to-XL C/C++ 选项映射和默认值。您可以根据需求更改这些配置文件。参考 XL 编译器手册更多地了解 gxlc 和 gxlc++。
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,程序将因段错误而终止。
表 7 将关键 Solaris 线程函数与 pthreads 函数进行了比较。对于其他 Solaris 线程函数,请参阅 SUN 的 “Multithreaded Porting Guide”(见 参考资料)。
表 7. 线程和 pthreads 函数
Solaris 线程 API | Linux 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/A | pthread_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 列出了这些例程:
表 8. POSIX 线程例程
例程 | Solaris | Linux |
---|---|---|
pthread_cond_reltimedwait_np | y | n |
pthread_mutexattr_getrobust_np | y | n |
pthread_mutexattr_setrobust_np | y | n |
pthread_mutex_consistent_np | y | n |
pthread_mutex_reltimedlock_np | y | n |
pthread_rwlock_reltimedrdlock_np | y | n |
pthread_rwlock_reltimedwrlock_np | y | n |
pthread_attr_getaffinity_np | n | y |
pthread_attr_setaffinity_np | n | y |
pthread_cleanup_pop_restore_np | n | y |
pthread_cleanup_push_defer_np | n | y |
pthread_getattr_np | n | y |
pthread_kill_other_threads_np | n | y |
pthread_rwlockattr_getkind_np | n | y |
pthread_rwlockattr_setkind_np | n | y |
pthread_timedjoin_np | n | y |
pthread_tryjoin_np | n | y |
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。表 9 显示了 Linux 中 POSIX 线程属性的默认值:
表 9. Linux 中 POSIX 线程属性的默认值
属性 | 默认值 |
---|---|
scope | PTHREAD_SCOPE_SYSTEM |
detachstate | PTHREAD_CREATE_JOINABLE |
schedparam | 0 |
inhiritsched | PTHREAD_EXPLICIT_SCHED |
schedpolicy | SCHED_OTHER |
虽然许多应用程序将从 2.4 内核迁移到 2.6,而无需重新编译,但是增加 NPTL 可能需要在多线程应用程序中进行少量的修改。关于如何通过使用 LinuxThreads 将应用程序迁移到 NPTL 超出了本文的范围。LinuxDevices.com 上的文章 “Migrating to Linux kernel 2.6” 提供其他相关信息(见 参考资料)。
![]() ![]() |
![]()
|
性能调优
一旦在 Linux 中移植并成功执行了代码,需要完成性能监控和性能调优,以确保移植的代码在目标平台上可以正常执行。这一节将提供 Linux on POWER 中可以使用的工具列表,帮助您完成以上操作。
常用性能调优工具
两个常用的工具(sysstat 包 和 nmon)使得性能监控和调优更加容易:
sysstat 包包含用于基础性能监控的通用扩展工具,包括 mpstat、iostat 和 sar 等工具。
nmon 将各种经典的系统监控工具集成到一个一站式的数据收集工具中。此外,还包含许多方便使用的后期处理插件,可用于收集、绘制和合并信息。
OProfile
Oprofile 基于硬件相关事件(比如缓存遗漏或 CPU 周期)提供代码的配置文件。例如,Oprofile 可以帮助确定是哪个源例程导致大部分缓存遗漏。Oprofile 使用包括 IBM POWER4?、POWER5?、POWER6? 和 PowerPC? 970 在内的许多 CPU 中提供的硬件性能计数器。有关的