移植过程本身非常简单:
- 清理代码和头文件,并删除与体系结构相关的部分和非标准做法。
- 编译代码,并修正在编译过程中发现的问题。
- 如果需要,则修正段故障及未对齐的访问。
- 重新编译代码,如果需要,则重复上面的过程。
移植 Solaris 应用程序至少需要两个平台:一个源平台和一个(或多个)Linux 目标平台。在将应用程序移植到 Linux 目标平台上运行时,您应该考虑很多因素:
- 要移植到哪个 Linux 目标平台?举例来说,有很多种硬件平台都支持 Linux,而字节顺序是以目标系统(如大尾数和小尾数)为基础来决定的。要使用哪个 Linux 分发版和内核版本?(Red Hat、TurboLinux、Caldera、SuSE,还是其它的?)
- Linux 目标平台必须支持所有硬件需求吗?举例来说,是否要依赖第三方网卡?您是否需要支持网络和存储需求?
- Linux 目标平台上是否具备所有所需的第三方包(如类库)、中间件软件、应用程序服务器工具以及应用程序开发工具?目标平台是否支持这些产品的相同(或兼容)版本?
- 在移植到目标平台的过程中是否要作出某些改变?举例来说,应用程序是否要改为使用其它数据库系统?
- 32 位的 Solaris 应用程序是否需要移植到 64 位的 Linux 平台上?
- Solaris 应用程序是否利用亲和处理器集?Solaris 有内核 hook(一种库)和用于将进程分配到特定处理器集的命令行工具。而 Linux 中没有与此相当的东西。
- 是否需要单独的公共源代码库?举例来说,您是否需要支持多个平台?
- 理解应用程序体系结构将使移植过程容易一些。举例来说,它是客户机/服务器模型还是 n 层应用程序组件?您需要决定先移植哪个软件组件。
如果只有硬件和操作系统被改变了,那么您进行的就是真正的移植。您可以使用不同的方法来实现目标。下面的选项描述了移植方法的两个步骤:
- 选项 A:因为 Sparc 平台也支持 Linux,所以被修改的代码可以在移植到 Linux 目标平台上之前在 Sparc 平台上测试和执行。您可以从 Sun 免费获得一套全面的用于 Solaris 操作环境的软件,它们在 Linux 和 Solaris 环境中都能够提供互操作性和通用性。
Sun 的开发工具使创建兼容的源代码更容易,并有助于迁移极为重要的实用程序代码以避免重写,还可以减少安装时间。这个选项可以帮助 Linux 经验有限的开发者将 Solaris 源代码移植到 Linux 目标平台上。下面的 Web 站点提供了 Solaris GNU 工具:
http://www.sun.com/solaris/freeware.html
在构建和测试了被移植的代码之后,您就可以遵循下一个选项来将代码移植到 Linux 目标平台了。
- 选项 B:将代码移植到 Linux 目标平台。将测试结果与 Solaris 平台作比较,以验证操作是否正确。修改过的代码必须是与尾数无关的,或者必须使用条件编译以支持小尾数平台。
要开始移植,请仔细检查 Solaris 源代码,并测试这些代码以保证其运行无误。检查过程应该包括完整的编译、重构建以及测试周期。大多数情况下,代码在 Linux 目标系统上会出错,因为最初的源代码树中安装了有错误的软件更新。因此,除非您亲自测试过代码,否则不能轻信在移植时通常会听到的断言(“它在源系统上没有问题!”)。
下面的参考资料也有助于您进行 Linux 移植:
- 由 Malcom Zung 和 Brian Thomas 撰写的 Solaris 移植指导文章,它位于:
ibm.com/developerworks/library/l-solar/ - Red Hat 的 Solaris-to-Linux Porting Guide是一份对移植信息的很好的总结,它位于:
http://www.redhat.com/devnet/whitepapers/solaris_port/book1.html - 关于 zSeries 平台的文章 Porting Unix applications to Linux,它位于:
ibm.com/servers/eserver/zseries/library/techpapers/gm130115.html
下面的建议不是将 Solaris 应用程序移植到 Linux 环境的唯一方法。您可以运用自己的移植经验来建立自己的移植方法。
选择移植开发平台
Sparc 平台:这种方法能够让 Solaris 开发者轻松地实现迁移,将 Linux 应用程序移植到其它目标平台上。因为 Sun 提供了公共库和构建环境,从而将开发在 Linux 和 Solaris 操作环境上兼容的源代码的过程流水线化,所以第一步是修改 Solaris 平台上的移植代码。这些工具包括下面的使用指南:
- 如何识别出 C/C++ 源代码中可能出现的库调用区别。
- 如何转换 shell 脚本。
- 如何在 Solaris 下重新创建 Linux 系统配置数据。要了解更多信息,请参阅:
http://www.sun.com/software/linux/compatibility/ultralinux/
在 Solaris 平台上测试和构建了 Linux 应用程序之后,您就可以移植到 Linux 目标平台上了。这种方法有下面几个优势:
- 它使改变之处减到最少,从而让这些改变更容易理解,更容易管理,也更容易推广。
- 它使移植过程中产生的新问题的数目减到最少。
- 每一处改变都修正了某个已知的问题。
Linux 目标平台:经验丰富的 Linux 开发者可以在 Linux 目标平台上修改要移植的代码。您需要确保安装了正确版本的库和编译器,如 gcc、tcl/tk、glib、GNOME 和 KDE。
使用 grep 命令
在确定移植开发平台之后,您需要搜索下面内容,它们可能会导致源代码中出现移植问题:
- 正则表达式
- printf、sprintf、scanf 和 sscanf 例程的宏
- 可能改变数据排列的结构和联合
- #else .... #endif 语句
- 系统头文件(如 limits.h、types.h 等等)
搜索了可能产生的问题之后,您需要决定是否希望保留单独的源代码库,以支持多平台(Solaris、Linux 及其它平台)。
找出潜在的问题
在使用 grep 命令之后,您还需要检查是否存在低效率或不可移植的代码。下面的列表可以帮助您找出移植问题:
- 找出源代码和库中不兼容的地方。
- 实行比编译器所用的更严格的类型检查规则。
- 找出潜在的与变量有关的问题。
- 找出潜在的与函数有关的问题。
- 找出与流程控制有关的问题。
- 找出可能产生错误或降低效率的合法构造。
- 找出未使用的变量和函数声明。
- 找出有可能不可移植的代码。
使用移植工具
下面的工具已经可供使用,或者已在开发之中:
- 移植管理器(Porting Manager)是一个 Perl 脚本,它接受源代码树作为输入。它扫描 C/C++ 代码以找出仅用于 Solaris 的 API,并将它们标记出来。它还提供了文档,描述如何将 Solaris API 移植为 Linux 上与其相当的 API。扫描过程是基于表的(工具提供了要检查的 API 的表以及标记)。它将生成工具要检查的 API 的列表。它还会检查头文件和某些编译指示(pragma)。移植管理器有一个 GUI 前端,而且因为它是用 Perl 编写的,所以能够同时在 Solaris 和 Linux 上运行,估计在 Perl 运行的任何平台上都能够运行。
- developerWorks Solaris-to-Linux 移植工具是一种 Web 应用程序,它检查 Solaris 应用程序使用的 API 在 Linux 上的兼容性如何。下面的 Web 站点提供对该 Web 应用程序工具的访问:
- MigraTEC 移植套装由 MigraTEC 开发,这家公司专门开发在平台之间迁移应用程序的工具。该移植套装包括 Migration WorkBench,它提供了一个完整的从 Solaris 到 Linux 的 API 映射:
修正编译时找出的问题
通常,要重复好几次才能够编译出没有问题的代码。请确保使用了 -Wall 选项,以捕获所有警告消息。
测试和调试程序
您可以使用 gdb 工具来调试修改过的代码。要了解更多关于 gdb 的信息,请参阅 GNU gdb 调试程序。
这一部分将讨论目录、文件系统、信号、尾数问题、系统派生的数据类型以及绝对地址。
目录
Linux 目录的结构是标准化的。每个目录都有定义精确的任务。典型的 Solaris 和 Linux 安装将创建如下表所示的目录:
Solaris 目录 | 功能 | Linux 目录 | 功能 |
/bin | 用户命令。 | /bin | 包含普通用户启动时和启动后所需的二进制文件。 |
/boot | 包含开始启动过程的 LILO 引导程序所需的文件。内核文件驻留在 /boot 中。 | ||
/dev | 包含 /devices 中的符号链接条目,大多数真正的设备条目都是在 /devices 中创建的。 | /dev | 包含真正的块、字符和其它指向设备的设备文件,如 fd0(第一个软盘驱动器)和 hda1(第一个硬盘驱动器上的第一个分区)。 |
/devices | 对于系统中的每个真正的设备来说,都应该在该目录中为其创建一个条目。 | ||
/etc | 其它命令、网络配置文件和脚本等等。 | /etc | 预留给属于机器本身的配置文件。/etc 中没有二进制文件。X-Windows 配置文件 XF86Config 存储在 /etc/X11 中。 |
/home | 通常用作用户主目录。 | /home | 通常用作用户主目录。 |
/kernel | 包含内核模块。 | 对应的内核文件在 /boot 和 /lib 模块中。 | |
/opt | 应用程序包。 | ||
/platform | 特定于平台的 Unix 和用于启动的设备驱动程序。 | ||
/proc | 包含关于活动进程和线程的信息,还提供一个接口来控制这些进程和线程。每个进程目录都有一些文件条目,包含该进程的内核结构。 | /proc | 包含深入到内核的文件系统视图。对于每个进程,都有多个目录,另外,还有对应于系统计数器和限制的目录和文件。 |
/lib | 只包含那些需要执行 /bin 和 /sbin 中的二进制文件的库。/lib/modules 包含可装入的内核模块。 | ||
/lost&found | |||
/mnt | 系统管理员进行临时安装所用的安装点。 | ||
/root | 这是 root 用户的主目录,除了 root 用户的概要文件信息之外,里面通常没有任何重要文件。 | ||
/sbin | 系统控制命令,例如 mount 和 umount。 | /sbin | 只被 root 用户使用的可执行文件,而且只是那些需要安装 /usr 并执行系统恢复操作的可执行文件。 |
/tmp | 它也是一个映射到系统内存的特殊文件系统。 | /tmp | 与 Solaris 系统相同 |
/usr | 编译器,管理性质的。 | /usr | 多数应用程序软件安装所在的位置。 |
/var | 包括 lpd 和 mail spool。也被各种需要记录日志文件的应用程序(如系统消息)所使用。 | /var | 包括 lpd 和 mail spool。也被各种需要记录日志文件的应用程序所使用。 |
文件系统
Linux 可以使用几种类型的文件系统。每种文件系统都有自己的格式和一组特征(如文件名长度、文件大小的最大值等等)。Linux 还支持几种第三方文件系统类型,如 MS-DOS 文件系统。下面的表列出了 Linux 可以使用的各种文件系统类型:
文件系统 | 类型名 | 注释 |
Second Extended Filesystem | ext2fs | 最常见的 Linux 文件系统 |
Third Extended Filesystem | ext3fs | ext2fs 的日志记录文件系统 |
Extended Filesystem | ext | 已被 ext2fs 取代 |
Minix Filesystem | minix | 最早的 Minix 文件系统 |
Xia Filesystem | Xia | 与 ext2 类似 |
UMSDOS Filesystem | umsdos | 用于在 DOS 分区上安装 Linux |
MS-DOS Filesystem | msdos | 使用 tp 处理 MS-DOS 文件 |
/proc Filesystem | proc | 提供系统信息 |
System V Filesystem | sysv | 用于访问系统 V |
HPFS Filesystem | hpfs | HPFS 分区的只读访问 |
Journal Filesystem | jfs | 有日志记录的文件系统 |
ReiserFS Filesystem | reiserFs | 使用经典的平衡树(balanced tree)算法上的变量 |
最常用的文件系统类型是 Second Extended Filesystem,或者说 ext2fs。ext2fs 是最有效、最灵活的一种文件系统;它允许文件名长达 256 个字符,文件系统大小最多可以达到四千兆字节。您还可以将 ext2fs 升级为 ext3fs。您可以在 /proc/filesystem 目录的文件中找到您的内核目前支持哪些文件系统。
字节顺序问题
SPARC 体系结构是big endian(BE)的,它具有向前的字节顺序。第 0 位是最不重要的位,而第 0 字节是最重要的字节。请注意,在从 Solaris 向 pSeries 或 IBM eServer zSeries 移植时,字节顺序问题并不会产生影响,因为它们都是 BE 平台。Intel x86 体系结构是little endian(LE),所以第 0 字节是最不重要的字节(LSB)。图 1 说明了 BE 和 LE 表达方式的字节顺序:
数据类型不匹配
当把 4 字节的整数当作整数数据类型的数据元素对待时,LE 和 BE 则按相反方向查看整数中的各个位和字节。
|
使用与尾数无关的解决方案可以解决 LE 和 BE 之间的字节顺序问题。
|
网络通信
如果要在 BE 和 LE 系统之间转移多字节值的数据,那么我们要做的就只是提供交换字节的代码。对于网络通信来说,象 htons() 和 ntohs() 这样的函数也用来将端口号转换为网络字节顺序。
常用的尾数解决方案指南
- 使用与尾数无关的解决方案来解决数据类型不匹配问题。
- 使用宏和指令。
要使代码能够移植,您可以使用如下的宏和条件编译指令:
#define BIG_ENDIAN 0 #define LITTLE_ENDIAN 1 #define BYTE_ORDER BIG_ENDIAN
- 使用编译时选项。
另一种可以实现这一点的选项是在编译命令行定义 BYTE_ORDER 的值。您可以只改变 makefile 来构建应用程序,也可以使用 GCC 编译选项来选择合适的特定于尾数的代码片段。
信号
Linux 接受四种缺省操作作为信号:忽略、停止进程、终止进程或在终止进程之后生成核心转储。Linux 支持由 System V、BSD 和 POSIX 提供的几乎每种信号,除了下面的特殊情况:
- 不支持 SIGEMT、SIGINFO 和 SIGSYS。
- SIGABRT 与 SIGIOT 相同。
- SIGIO、SIGPOLL 和 SIGURG 相同。
- SIGBUS 被定义为 SIGUNUSED。从技术角度上讲,Linux 环境中没有“总线错误”。
下面的 web 站点提供了关于 Linux 信号的更多详细信息:
http://www.linux-mag.com/2000-01/compile_01.html
http://www.linuxhq.com/guides/LPG/node138.html - SECTION001120000000000000000
注意:POSIX.1 只定义 SA_NOCLDSTOP。当移植使用信号操作的应用程序时,您可能必须修改 sa_flag 的值来获取合适的行为。
Solaris 中有 sig2str() 和 str2sig() API,它们用于在信号和字符串之间对信号名进行来回转换。因为 Linux 不支持这些 API,所以下面的代码对移植不起作用:
|
下面是修正后的用于向 Linux 进行移植的代码:
|
下面的表列出了 Solaris 和 Linux 操作系统中常用的信号。
Solaris | Solaris 缺省操作 | Linux | Linux 缺省操作 |
SIGHUP | 终止 | SIGHUP | 忽略 |
SIGINT | 终止 | SIGINT | 忽略 |
SIGQUIT | 终止,核心 | SIGQUIT | 终止,核心 |
SIGILL | 终止,核心 | SIGILL | 终止,核心 |
SIGTRAP | 终止,核心 | SIGTRAP | 忽略 |
SIGABRT | 终止,核心 | SIGABRT | 终止,核心 |
SIGEMT | 终止,核心 | SIGEMT | Linux 上不支持 |
SIGFPE | 终止,核心 | SIGFPE | 终止,核心 |
SIGKILL | 终止 | SIGKILL | 终止 |
SIGBUS | 终止,核心 | SIGBUS | 终止,核心 |
SIGSEGV | 终止,核心 | SIGSEGV | 终止,核心 |
SIGSYS | 终止,核心 | SIGSYS | Linux 上不支持 |
SIGPIPE | 终止 | SIGPIPE | 忽略 |
SIGALRM | 终止 | SIGALRM | 忽略 |
SIGTERM | 终止 | SIGTERM | 终止 |
SIGUSR1 | 终止 | SIGUSR1 | 忽略 |
SIGUSR2 | 终止 | SIGUSR2 | 忽略 |
SIGCHLD | 忽略 | SIGCHLD | 忽略 |
SIGPWR | 忽略 | SIGPWR | 忽略 |
SIGWINCH | 忽略 | SIGWINCH | 进程停止 |
SIGURG | 忽略 | SIGURG | 忽略 |
SIGPOLL | 终止 | SIGPOLL | Linux 上不支持 |
SIGSTOP | 进程停止 | SIGSTOP | 进程停止 |
SIGSTP | 进程停止 | SIGSTP | 进程停止 |
SIGCONT | 忽略 | SIGCONT | 忽略 |
SIGTTIN | 进程停止 | SIGTTIN | 进程停止 |
SIGTTOU | 进程停止 | SIGTTOU | 进程停止 |
SIGVTALRM | 终止 | SIGVTALRM | 终止,核心 |
SIGPROF | 终止 | SIGPROF | 忽略 |
SIGXCPU | 终止,核心 | SIGXCPU | 终止,核心 |
SIGXFSZ | 终止,核心 | SIGXFSZ | 终止,核心 |
SIGWAITING | 忽略 | SIGWAITING | Linux 上不支持 |
SIGLWP | 忽略 | SIGLWP | Linux 上不支持 |
SIGFREEZE | 忽略 | SIGFREEZE | Linux 上不支持 |
SIGTHAW | 忽略 | SIGTHAW | Linux 上不支持 |
SIGCANCEL | 忽略 | SIGCANCEL | Linux 上不支持 |
SIGRTMIN | 终止 | SIGRTMIN | Linux 上不支持 |
SIGRTMAX | 终止 | SIGRTMAX | Linux 上不支持 |
系统派生的数据类型
系统派生的数据类型可以有不同的字节大小。派生的数据类型就是用 typedef 定义的派生类型,或者用已知基本类型的结构定义的类型。下面几部分将比较 Solaris 和 Linux 中最常见的系统派生的数据类型。
- 数据类型 gid_t、mode_t、pid_t 和 uid_t
数据类型 gid_t 用来代表用户的组 ID,而 uid_t 用来代表用户 ID。数据类型 mode_t 用来表示文件的模式,而 pid_t 用来以唯一的数字标识不同的进程。
OS gid_t mode_t pid_t uid_t Solaris long unsigned long long long Linux unsigned int unsigned int int unsigned int - Size_t、ssize_t 和 wint_t
数据类型 size_t 和 ssize_t 应该结合内存中对象的大小来使用,并返回字节计数或错误指示。当程序使用多字节和宽字符子例程时,就需要数据类型 wint_t 来表示宽字符代码值以及文件尾标记。
OS size_t ssize_t wint_t Solaris unsigned int int long Linux unsigned long int unsigned int
绝对地址
每个平台都可能使用不同的内存位置来存储程序堆栈、系统库、堆等等。因此,使用硬编码地址已经很难保证到其它系统的可移植性了。某些寻址方式还忽略高阶位,所以硬编码地址 0x80000000(高阶位是 1)将被转换为 0x00000000,这很可能是非法的,而且绝对不是您想要的。在生成地址的时候,对地址的算术运算也能使高阶位变为 1。
如果应用程序使用了导致分段冲突或其它错误的绝对地址,那么就必须改变它。/proc/<pid>/map 将展示一个活动进程如何使用它内部地址空间的存储范围。如果代码必须使用绝对地址,那么这个 map 就可以用来查找还没有被保留的地址范围。
C/C++ 应用程序基础架构
操作系统 |
|
运行时库 |
|
C/C++ 应用程序工具的使用
分析和设计 |
|
IDE |
|
低级编辑/编译/调试 |
|
构建 |
|
打包 |
|
测试 |
|
性能调优及基准测试 |
|
源代码管理 |
|
维护和故障检测 |
|
Java 应用程序工具的使用
|
|
IDE |
|
JVM |
|
编译器 |
|
性能工具 |
|
源代码管理 |
|
维护和故障检测 |
|
Solaris make 和 GNU make 都定义了相同的基本后缀集和隐式规则。下面的目标、宏和变量都是相同的:
- 目标:.DEFAULT、.IGNORE、.PRECIOUS、.SILENT 和 .SUFFIX
- 内部宏:$*、$%、$?、$<、$@、$$@、$(F) 和 $(D)
- 预定义的宏:ar、as、cc、ld、lex、lint、m2c、pc、rm -f 和 yacc
- 环境变量:MAKEFLAGS
- 变量:CC、CFLAGS、CPPFLAGS、FC、FFLAGS、LEX、LFLAGS、YACC、YFLAGS、LD 和 LDFLAGS
注意:GNU make 不完全支持 %。只有第一个 % 才会被替换。
下面是示例:
|
在 Sun 上,make 命令将打印:
|
在使用 GNU 的 Linux 上,make 命令将打印:
|
为了解决这个差别带来的问题,您需要按下面所示修改代码:
|
make 的后缀
make 自带的内置后缀规则让开发者可以极大地简化 makefile。下面的单后缀和双后缀的表包含 Solaris 和 GNU make 命令的缺省推理规则。
单后缀推理规则
后缀 | Make | 单后缀规则 |
.c | Solaris | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBD) |
GNU | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LOADLIBES) $(LDLIBS) -o $@ |
|
.C | Solaris | $(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) |
GNU | $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LOADLIBES) $(LDLIBS) -o $@ |
|
.cc | Solaris | $(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) |
GNU | $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $(LOADLIBES) $(LDLIBS) -o $@ |
|
.f | Solaris | $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) |
GNU | $(FC) $(FFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LOADLIBES)$(LDLIBS) -o $@ | |
.F | Solaris | $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) |
GNU | $(FC) $(FFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LOADLIBES)$(LDLIBS) -o $@ | |
.mod | Solaris | $(M2C) $(M2FLAGS) $(MODFLAGS)-o $@ -e $@ $< |
GNU | $(M2C) $(M2FLAGS) $(MODFLAGS) $(TARGET_ARCH) -o $@ -e $@ $^ | |
.p | Solaris | $(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) |
GNU | $(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LOADLIBES) $(LDLIBS) -o $@ |
|
.r | Solaris | $(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) |
GNU | $(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LOADLIBES) $(LDLIBS) -o $@ |
|
.sh | Solaris | $(RM) $@ cat $<>$@ chmod -x $@ |
GNU | cat $<>$@ chmod a+x $@ |
双后缀推理规则
后缀 | Make | 双后缀规则 |
.c.o | Solaris | $(CC) $(CFLAGS) $(CPPFLAGS) -c $(OUTPUT_OPTION) $< |
GNU | $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c$(OUTPUT_OPTION) $< | |
.c.ln | Solaris | $(LINT) $(LINTFLAGS) $(CPPFLAGS) $(OUTPUT_OPTION) -c $< |
GNU | $(LINT) $(LINTFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -C$* $< | |
.cc.o | Solaris | $(CCC) $(CCFLAGS) $(CPPFLAGS) -c $(OUTPUT_OPTION) $< |
GNU | $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c$(OUTPUT_OPTION) $< | |
.C.o | Solaris | $(CC) $(CFLAGS) $(CPPFLAGS) -c $(OUTPUT_OPTION) $< |
GNU | $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c$(OUTPUT_OPTION) $< | |
.def.sym | Solaris | $(M2C) $(M2FLAGS) $(DEFFLAGS) -o $@ $< |
GNU | $(M2C) $(M2FLAGS) $(DEFFLAGS) $(TARGET_ARCH) -o $@ $< | |
.f.o | Solaris | $(FC) $(FFLAGS) -c $(OUTPUT_OPTION) $< |
GNU | $(FC) $(FFLAGS) $(TARGET_ARCH) -c $(OUTPUT_OPTION) $< | |
.F.o | Solaris | $(FC) $(FFLAGS) $(CPPFLAGS) -c $(OUTPUT_OPTIONS) $< |
GNU | $(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c$(OUTPUT_OPTION) $< | |
.l.c | Solaris | $(RM) $@ $(LEX) $(LFLAGS) -t $< > $@ |
GNU | @$(RM) $@ $(LEX) $(LFLAGS) -t $< > $@ |
|
.l.ln | Solaris | $(RM) $*.c $(LEX) $(LFLAGS) -t $< > $*.c $(LINT) $(LINTFLAGS) $(CPPFLAGS) -o $@ -i $*.c $(RM) $*.c |
GNU | @$(RM) $*.c $(LEX) $(LFLAGS) -t $< > $*.c $(LINT) $(LINTFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -i $*.c -o $@ $(RM) $*.c |
|
.mod.o | Solaris | $(M2C) $(M2FLAGS) $(MODFLAGS) -o $@ $< |
GNU | $(M2C) $(M2FLAGS) $(MODFLAGS) $(TARGET_ARCH) -o $@ $< | |
.r.o | Solaris | $(COMPILE.r) $(OUTPUT_OPTION) $< |
GNU | $(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -c $(OUTPUT_OPTION)$< | |
.s.o | Solaris | $(AS) $(ASFLAGS) -o $@ $< |
GNU | $(AS) $(ASFLAGS) $(TARGET_MACH) -o $@ $< | |
.S.o | Solaris | $(AS) $(ASFLAGS) -o $@ $< |
GNU | $(CC) $(ASFLAGS) $(CPPFLAGS) $(TARGET_MACH) -c -o $@ $< | |
.y.c | Solaris | $(YACC) $(YFLAGS) $< mv y.tab.c $@ |
GNU | $(YACC) $(YFLAGS) $< mv -f y.tab.c $@ |
|
.y.ln | Solaris | $(YACC) $(YFLAGS) $< $(LINT) $(LINTFLAGS) $(CPPFLAGS) -o $@ -i y.tab.c$ (RM) y.tab.c |
GNU | $(YACC) $(YFLAGS) $< $(LINT) $(LINTFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -C$* y.tab.c $(RM) y.tab.c |
Sun Workshop(Forte)编译器和 GCC 都支持扩展 C 语言。Sun Workshop 编译器的扩展几乎等同于 GCC。扩展的编译指示的形式是独一无二的。GCC 通常不使用编译指示,但在使用了 -Wall 选项的时候会警告忽略了编译指示。
GCC 上的 -m486 选项用来编译 x486 机器的二进制文件,它只会改变某些特定的优化。有一种版本的 GCC 可以对 586 进行很好的优化,但它很不可靠,尤其是在高优化设置时。关于 Pentium GCC,请参阅:
ftp://tsx-11.mit.edu/pub/linux/ALPHA/pentium-gcc/。
对于 pSeries 和 RS/6000 来说,使用“-mcpu”选项将禁用“-mpower”或“-mpowerpc”选项。我们推荐的做法是使用缺省的“-mcpu=common”。
对于 SPARC 来说,使用“-mcpu”选项也会为机器类型选择体系结构。
GNU C 库也有线程意识。当您链接到线程处理库 libpthread(用 gcc 的命令行参数 pthread )时,libc.so 中的某些函数将被线程处理库中的函数覆盖。
下面的表包含 Solaris 和 GNU GCC 上的 C 编译器的常用选项。
Sun Workshop | GCC | 描述 |
-# | -v | 打开详细(verbose)模式,显示每个被调用的组件。 |
-Xa | -ansi | 指定与 ANSI/ISO 标准的兼容。GCC 支持所有 ISO C89 程序。您可以使用“-std”来指定特殊版本的 ISO C。 |
-xinline | -finline-functions | 只插入那些指定的函数。 |
-p | -p | 生成额外代码,用来写适用于分析程序简档的概要文件信息。 |
-xa | -ax | 生成额外代码,用来写基本块的概要文件信息,记录每个基本块被执行的次数。 |
-xspace | -O0 | 不进行优化。 |
-xunroll= | -finroll_loops | 只对迭代次数可以在编译时或运行时决定的循环进行循环打开优化。 |
-xtarget = name | -b=machine | 参数机器指定进行编译的目标机器。另外,这里的每个目标机器类型都可以有自己特殊的选项,以“m”开头,在各种硬件模型或配置中进行选择。 |
-xo | -O, -O1, -O2, -O3, -Os | 控制各种优化。 |
-O | 同上 | 控制各种优化。 |
-xmaxopt | 保证 GCC 不使用编译指示。 | |
-xnolib | -nostdlib | 缺省情况下不链接任何库。 |
-fsingle | -fsingle-precision-constant | 将浮点常量作为单精度常量对待,而不是隐式地将其转换为双精度。 |
-C | -C | 告诉预处理器不要废弃注释。与“-E”选项一起使用。 |
-xtrigraphs | -trigraphs | 支持 ISO C trigraph。 |
-E | -E | 预处理所有指定的 C 源文件,并将结果输出到标准输出或指定的输出文件。 |
-xM | -M | 只运行指定的 C 程序上的预处理器,要求生成 makefile 依赖性(dependencies)信息并将结果发送到标准输出。 |
-xpg | -pg | 生成额外代码,来写适用于分析程序 gprof 的概要文件信息。 |
-c | -c | 命令编译器不要用 ld 进行链接,并为每个源文件生成 .o 文件。 |
-o | -o | 命名输出文件。 |
-S | -S | 命令 cc 生成组装源文件但并不组装程序。 |
-xtemp | TMPDIR | 如果设置了 TMPDIR 环境,就指定临时文件要使用的目录。 |
-xhelp=f | -help | 显示联机帮助信息。 |
-xtime | -time | 报告编译序列中的每个子进程所使用的 CPU 时间。 |
-w | -q | 禁止编译器警告。 |
-erroff= %none | -W | 显示警告消息。 |
-errwarn | -Werror | 将所有警告消息转换为错误消息。 |
Sun 和 GNU 链接程序都接受为数众多的选项。下面的表解释了 SPARCworks 链接程序最常用的选项与 GNU 链接程序的功能对比。
Solaris Workshop | gld 选项 | 描述 |
-a | -static | 启用静态模式的缺省行为,防止与共享库进行链接。链接程序会创建可执行文件和导致错误消息的未定义符号。GNU ld 有 -static 选项,也能启用这种行为。 |
-b | 不用 -fPIC/-fpic 选项编译源代码,从而实现 GNU 链接程序的同等功能。 | |
-g | -g | 以操作系统的本机格式生成调试信息。 |
-G | -shared | 生成共享对象。与 GNU 链接程序等同的是 -shared。 |
-m | (-M) | 打印链接程序映射。-M 选项打印可以比较的某些东西,只是它们的格式不同,内容也稍有不同。 |
-s | -S/-s | 使用 GNU 链接程序的 -S(它只会删除调试信息),实现与 -s 选项同等的效果。 |
-h name | -soname name | 将名称设置为共享对象的名称。如果使用 GNU 链接程序,就必须用 -soname 选项。 |
-o filename | -o filename | 将输出放在文件中。不管产生的输出是何种类型,它都适用。缺省情况是将可执行文件放在“a.out”中。 |
-L directory | -L dir | 将目录 dir 添加到目录列表中。 |
-R path | -rpath path | 指定运行时链接程序的搜索方向。GNU 链接程序使用 -rpath 选项。 |
每个 Linux 共享库都被分配了一个叫作 soname的特殊名称,它包括库的名称和版本号。除非您有特别的理由,否则请不要链接到特定版本之外的库上。通常请使用 C 编译器或链接程序的标准的 -l libname选项。链接程序将查找文件 lib libname.so,该文件是到正确版本的库的符号链接。
特定于 pSeries 和 RS/6000 的 GCC 选项
选项 | 描述 |
-fPIC | 生成与位置无关的代码(position-independent code,PIC),以便在共享库中使用。 |
-mpower、-mno-power、-mpowerpc、-mno-powerpc、-mnpowerpc64、-mno-powerpc64 | 指定 GCC 将生成各种体系结构的指令。 |
-mcpu=cpu_type | 设置体系结构类型、注册程序的使用、助记符的选择和特定机器类型的指令调度参数。 |
-mtune=cpu_type | 设置特定机器类型的指令调度参数。 |
-mlittle | 用小尾数模式编译 Power PC 处理器的代码。 |
-mbig | 用大尾数模式编译 Power PC 处理器的代码。 |
-mcall-linux | 编译 Power PC 基于 Linux 的 GNU 系统的代码。 |
Solaris 线程库(Solaris Thread Library,STL)
为 Solaris 编写的应用程序经常会使用非标准的专有函数,这会令向 Linux 进行移植的工作很困难。Linux( Intel 平台)上支持一种临时的 STL,这将减少迁移这些应用程序的麻烦。它提供了基于 POSIX 线程库的 Solaris 线程 API 的实现。STL 是 Solaris 兼容性库(Solaris Compatibility Libraries,SCL)的一个组件,您可以按开放源代码的形式免费获取 SCL 的这个部分。您可以从 Compaq发起的 Sourceforge 项目获取它,这个项目位于:
http://sourceforge.net/projects/sctl
局限性
SCL STL 不支持下面的功能:
- 对系统级同步对象的支持
- 出现 fork() 时对每个线程的复制
要让开发者使用 STL 库,您需要留意线程的局限性和问题。举例来说,POSIX 线程的暂挂和线程继续没有得到支持。一篇题为“Building an Open-Source Solaris-Compatible Threads Library”( Proceedings of the FREENIX,2001 年 6 月)的文章中包含更多详细信息和实现注释。您可以从下面的 Web 站点获取该文章的副本:
http://www.opensource.compaq.com/the_source/linux_papers/scl_solaris.htm
我们希望今后 Linux 版本的 STL 能够支持其它 Linux 目标平台(RS/6000、Power PC 等等)。
下面这部分将讨论 Solaris 中使用了线程的应用程序、Solaris 中使用了 POSIX 线程的应用程序、Solaris 线程库和 POSIX 线程库的对比,以及对接口和定义的