从 Solaris 迁移到 x86 上的 Linux 指南 转自:IBMdeveloperWorks Solaris 被认为是风格和 Linux™ 最为接近的一种 UNIX®,但是对于程式的迁移来说,他们在诸如内存映射、线程连同对自然语言的支持等领域还是有很大区别的。这个移植指南能够为您在计划将程式移植到 Linux/x86 上时提供一些建议,并且帮助您理解研发环境和体系架构之间的区别。 内容: int get_stack(void **StackPtr) { *StackPtr = 0; __asm__ __volatile__ ("movl %%esp, %0": "=m" (StackPtr) ); return(0); } 移植规划 研发环境 体系架构和系统特有的区别 结束语 在 Solaris,下面这段示例代码让您能够获取堆栈指针: 清单 2. 在 Solaris 上获取堆栈指针 .section ".text" .align 8 .global my_stack .type my_stack,2 my_stack: ! Save stack pointer through 1st parameter st %sp,[%o0] ! Compute size of frame in return result reg retl sub %fp,%sp,%o0 .size my_stack,(.-my_stack) 在 Linux x86 上,您能够使用一个 compare 和 swap 操作来实现原子锁。例如,下面是 Linux x86 上使用 compare 和 swap 操作的一个简单实现: 清单 3. Linux x86 上的 Compare 和 swap 操作 bool_t My_CompareAndSwap(IN int *ptr, IN int old, IN int new) { unsigned char ret; /* Note that sete sets a 'byte' not the word */ __asm__ __volatile__ ( " lock/n" " cmpxchgl %2,%1/n" " sete %0/n" : "=q" (ret), "=m" (*ptr) : "r" (new), "m" (*ptr), "a" (old) : "memory"); return ret; } 在 Solaris SPARC 上,加锁的原子操作能够如下实现: 清单 4. Solaris 上的原子锁 .section ".text" .align 8 .global My_Ldstub .type My_Ldstub,2 My_Ldstub: ldstub [%o0],%o0 ! Atomic load set sll %o0,24,%o0 ! Zero fill ... retl ! ... result register srl %o0,24,%o0 ! ... and retrn .size My_Ldstub,(.-My_Ldstub) 字节顺序(endianness) 由于 SPARC 采用的是 big-endian,而 x86 采用的是 little-endian,因此您需要考虑 endianness 的问题。 Endianness 的问题在以下这些情况中都变得很重要:移植客户机要在异构网络环境中进行通信的应用程式,将数据永久地保存到磁盘上,产品跟踪(在 SPARC 平台上所生成的跟踪信息必须能够在 x86 平台上正确地格式化),连同其他相关领域。IA-64 Linux 内核默认使用 little-endian,但是也能够使用 big-endian 的字节顺序。 系统调用 Solaris 系统调用使用了一种在 Linux 上是不可用的不同的命名和签名机制,这在“从 Solaris 向 Linux on POWER 迁移指南” 中已介绍过了。以下这些系统调用现在在 RHEL4 中能够使用: * Waitid * Putmsg * putpmsg * Getmsg * getpmsg Curses 库 Linux 用于 xurses 库的那些库函数比 Solaris 更加类似于 AIX。例如,诸如 addwch 之类的函数在 Linux 中不受支持。有些函数,例如 mvchgat,在 Solaris 中不可用。当您将和 curses 有关的代码从 Solaris 移植到 Linux 上时,很多代码都需要重写。 Terminal IO Solaris 中的 termio 结构和 Linux 中的这个结构是不同的。Linux 中的 termio 结构多了几个域,您能够使用他们来配置输入和输出速度。 Linux 中 termio 结构的定义位于 /usr/include/bits/termios.h 文档中,而 Solaris 中 termio 的定义则位于 /usr/include/sys/termios.h 文档中。 IOCTL 执行 ioctl 能够使用的选项在 Solaris 和 Linux 中是不同的。例如,要获得资源的利用情况,您能够向 ioctl 系统调用传递 PIOCUSAGE: Return_code = ioctl("/proc/
", PIOCUSAGE, &buff) ; 在 Linux 中,您必须直接从 /proc/
目录中读取相关的文档来获得相关信息,在 Linux 上不能使用 PIOCUSAGE 选项。在这两种情况中,pid 是正在执行命令的当前进程的进程 id。 另外一个例子是当您希望获取该进程的堆信息时。在 Solaris 中,要获取堆信息,您能够使用下面的方法: Return_code = Ioctl("/proc/
",PIOCPSINFO,&psinfo) 在这段代码中,psinfo 是个 struct prpsinfo 结构;Prpsinfo.pr_size 就给出了该进程的堆大小。 在 Linux 中,正在使用的页数能够从 /proc/
/mem 和 /proc/
/stat 中获得。页面大小能够使用系统调用 getpagesize 获得。这两个值(页数和页面大小)的乘积就是堆的当前大小。 Getopt 在 Linux 中,只有在配置了 POSIXLY_CORRECT 环境变量之后,getopt 调用才遵循 POSIX 标准。 假如配置了环境变量 POSIXLY_CORRECT,那么对参数的分析一碰到非选项参数(以“-”开始的参数)就立即停止。 其余的参数全部被解释为非选项参数。 调用 shell 脚本之间的区别 假如 shell 脚本使用了 su 命令,就会派生一个子 shell。这种行为和 Solaris 不同,在 Solaris 中, su 命令不会派生一个新 shell。 假如您在 Linux 上执行命令 su - root,那么 ps 命令的输出就会如下所示: 30931 pts/4 00:00:00 su 31004 pts/4 00:00:00 ksh 在这段代码中,30931 是进程 31004 的祖先进程。您可能要修改一些受到这种关系影响的脚本。 重启动时的设备处理 从 RHEL4 开始,Linux 就采用了热插拔子系统 udev 的概念。他提供了可配置的动态设备命名的支持。设备配置是在每次重新启动时动态构建的。 例如,假如您有一个新目录 /dev/dsk,那么系统可能并不知道他的存在,这个目录在重启之后可能就会丢失。为了避免 /dev/dsk 目录丢失的问题,能够建立一个 /etc/udev/devices/dsk 目录,这样系统在重新启动时就能够维护这种信息,因此即使系统重新启动之后,/dev/dsk 也依然会存在。 内核参数 在 Solaris 中,内核参数能够在 /etc/system 文档中进行配置。在 Linux 中,内核参数能够使用 sysctl 系统调用进行修改。能够修改的参数在 /proc/sys/kernel 中都能够看到,procfs 的支持对于 sysctl 正常工作来说是必需的。 信号 信号之间的区别在“Technical guide for porting applications from Solaris to Linux”中已周详介绍过了。其他值得注意的一些区别是 Solaris 有 38 个信号,而 Linux 使用了 31 个信号,而且sigaction 结构也不相同。例如,在 Linux 中,sigaction 接口如下所示: 清单 5. Linux 中的 sigaction 结构 struct sigaction { /* Signal handler. */ #ifdef __USE_POSIX199309 union { /* Used if SA_SIGINFO is not set. */ __sighandler_t sa_handler; /* Used if SA_SIGINFO is set. */ void (*sa_sigaction) (int, siginfo_t *, void *); } __sigaction_handler; # define sa_handler __sigaction_handler.sa_handler # define sa_sigaction __sigaction_handler.sa_sigaction #else __sighandler_t sa_handler; #endif /* Additional set of signals to be blocked. */ __sigset_t sa_mask; /* Special flags. */ int sa_flags; /* Restore handler. */ void (*sa_restorer) (void); }; 在 Solaris 中,sigaction 结构如下所示: 清单 6. Solaris 中的 sigaction 结构 struct sigaction { int sa_flags; union { #ifdef __cplusplus void (*_handler)(int); #else void (*_handler)(); #endif #if defined(__EXTENSIONS__) || ((__STDC__ - 0 == 0) && !defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE)) || (_POSIX_C_SOURCE > 2) || defined(_XPG4_2) void (*_sigaction)(int, siginfo_t *, void *); #endif } _funcptr; sigset_t sa_mask; #ifndef _LP64 int sa_resv[2]; #endif }; 由于所支持的信号个数不同,siginfo_t 结构也不相同。对于 Linux,这能够在 /usr/include/bits/signal.h 文档中找到;对于 Solaris,这能够在 /usr/include/sys/siginfo.h 文档中找到。 线程支持和 IPC 有关 Linux on POWER 和 Solaris 之间进行移植的出版物已介绍了 Solaris 线程和 Linux 的 POSIX 线程(由 NPTL 库提供)之间的区别。 我的经验是,假如应用程式早已在 Solaris 上就使用了 POSIX 线程,那么他们移植到使用基于 NPTL 线程的 Linux 上就很简单。在 Linux 上还能够利用一些新特性,例如进程共享互斥锁,因此 Solaris 中使用 pthread 互斥锁和条件变量的那些有关 IPC 机制的代码在 Linux 中都能够使用。 自然语言的支持 在 Solaris 中对于自然语言的支持的代码也和 Linux 中可能会有所不同,或他们只是名字有所不同而已。在 Solaris 中大部分的 locales 和代码页在 Linux 中都有对应的内容。 结束语 从 Solaris 到 x86 上的 Linux 的移植工作在大部分情况中只是个重新编译的过程,或对编译器/链接器开关稍微进行一些修改。有时可能需要对某些平台特有的内容进行一些修改,例如加锁、内存映射、线程,等等。您应该了解一下这些差异,并对移植好好进行一下规划,从而减少移植所需要的时间。 参考资料 * 您能够参阅本文在 developerWorks 全球站点上的 英文原文。 * 下载 XL C/C V7.0 编译器。 * GNU binutils 用来和 XL C/C 和 GCC 一起生成对象。在 GNU Binutils 站点中有周详的解释。 * 下载 IBM Developer Kit for Linux, Java Technology Edition version 1.4.2。 * IBM 红皮书 AIX 5L Porting Guide 周详介绍了在将应用程式从其他基于 UNIX 的平台上移植到 AIX 5L 操作系统上所可能碰到的问题。 * Multithreaded Programming Guide 介绍了 POSIX 和 Solaris 线程 API,使用同步对象进行编程连同编译多线程程式的内容。 * Migrating to Linux kernel 2.6 讨论了将现有应用程式迁移到 2.6 版本的内核和专有 POSIX 线程库(NPTL)的问题。 * 下载 Performance Inspector,并获取有关该工具的更多信息。 * 访问 OProfile 的 Web 站点。 * 访问 RPM.org,阅读有关 RPM 的更多信息。 * developerWorks 迁移专题 能够帮助研发者寻找工具、技术文章、在线教程、课程、论坛、Web 站点、定制服务连同其他形式的帮助,从而帮助他们快速实现各种硬件平台向 Linux 的迁移。 * “Technical guide for porting applications from Solaris to Linux, version 1.0”(developerWorks,2002 年)介绍了更多有关 Solaris 和 Linux 之间的区别,连同将应用程式从 Solaris 移植到 Linux 上的内容。 * Solaris to Linux Porting Guide 讨论了从 7.x 系列的 Red Hat Linux 发行版本到 Solaris 移植的问题。 * “将企业应用程式从 UNIX 移植到 Linux”(developerWorks,2005 年)讨论了在进行大型移植实践时要考虑的问题。 * “从 Solaris 向 Linux on POWER 迁移指南 ”(developerWorks,2005 年)是对本文的补充,其中提供了移植到基于 POWER 的系统上的 Linux 的一些指导方针和资源。 * “用 OProfile 完全了解性能”(developerWorks,2003 年)介绍了如何使用这个工具来分析性能,而不考虑硬件和软件之间的异常交互。 * 在 developerWorks Linux 专区 中能够找到更多为 Linux 研发者准备的资源。