现在的位置: 首页 > 综合 > 正文

从 Solaris 向 Linux 移植应用程序的技术指导,版本 1.0

2013年10月14日 ⁄ 综合 ⁄ 共 16194字 ⁄ 字号 评论关闭

移植概述

移植过程本身非常简单:

  • 清理代码和头文件,并删除与体系结构相关的部分和非标准做法。
  • 编译代码,并修正在编译过程中发现的问题。
  • 如果需要,则修正段故障及未对齐的访问。
  • 重新编译代码,如果需要,则重复上面的过程。

移植指导

移植 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

    http://www.sunfreeware.com

    在构建和测试了被移植的代码之后,您就可以遵循下一个选项来将代码移植到 Linux 目标平台了。

  • 选项 B:将代码移植到 Linux 目标平台。将测试结果与 Solaris 平台作比较,以验证操作是否正确。修改过的代码必须是与尾数无关的,或者必须使用条件编译以支持小尾数平台。

要开始移植,请仔细检查 Solaris 源代码,并测试这些代码以保证其运行无误。检查过程应该包括完整的编译、重构建以及测试周期。大多数情况下,代码在 Linux 目标系统上会出错,因为最初的源代码树中安装了有错误的软件更新。因此,除非您亲自测试过代码,否则不能轻信在移植时通常会听到的断言(“它在源系统上没有问题!”)。

下面的参考资料也有助于您进行 Linux 移植:

 

移植过程

下面的建议不是将 Solaris 应用程序移植到 Linux 环境的唯一方法。您可以运用自己的移植经验来建立自己的移植方法。

选择移植开发平台

Sparc 平台:这种方法能够让 Solaris 开发者轻松地实现迁移,将 Linux 应用程序移植到其它目标平台上。因为 Sun 提供了公共库和构建环境,从而将开发在 Linux 和 Solaris 操作环境上兼容的源代码的过程流水线化,所以第一步是修改 Solaris 平台上的移植代码。这些工具包括下面的使用指南:

在 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 应用程序工具的访问:

    ibm.com/developerworks/linux/tools/l-solar.html

  • MigraTEC 移植套装由 MigraTEC 开发,这家公司专门开发在平台之间迁移应用程序的工具。该移植套装包括 Migration WorkBench,它提供了一个完整的从 Solaris 到 Linux 的 API 映射:

    http://www.migratec.com/MigraTEC/migration_suite.htm

修正编译时找出的问题

通常,要重复好几次才能够编译出没有问题的代码。请确保使用了 -Wall 选项,以捕获所有警告消息。

测试和调试程序

您可以使用 gdb 工具来调试修改过的代码。要了解更多关于 gdb 的信息,请参阅 GNU gdb 调试程序。

 

比较 Linux 与 Solaris

这一部分将讨论目录、文件系统、信号、尾数问题、系统派生的数据类型以及绝对地址。

目录

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 表达方式的字节顺序:

图 1. 大尾数和小尾数地址

数据类型不匹配

当把 4 字节的整数当作整数数据类型的数据元素对待时,LE 和 BE 则按相反方向查看整数中的各个位和字节。


int a=0x11121314;
char b, *ptr;
ptr= (char *) &a;	     // pointer 
        ptr points to 
        a
b= ptr[1];		     // 
        b is 0x13 in LE and 0x12 in BE

      

使用与尾数无关的解决方案可以解决 LE 和 BE 之间的字节顺序问题。


#define INTB16TO23(a) ((a>>16) & 0xff)
b= INTB16TO23(a);		// 
        b is 0x12 in BE and LE

      

网络通信

如果要在 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,所以下面的代码对移植不起作用:



#include<signal.h>
#include<stdlib.h>
#include<stdio.h>

int main(int argc, char **argv)
{
	char signame[SIG2STR_MAX];
	
        sig2str(atoll(argv[1]), signame);
	printf("Received signal = %s /n", signame);
	return 0;
}
      

下面是修正后的用于向 Linux 进行移植的代码:



#include<signal.h>
#include<stdlib.h>
#include<stdio.h>

int main(int argc, char **argv)
{
	const char *str
	
        int signo_ = atoll(argv[1]);
	str = sys_siglist[signo_];
	printf ("Received signal = %s /n", str);
	return 0;
}
      

下面的表列出了 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++ 应用程序基础架构

操作系统
  • Red Hat Linux V7.1
  • SuSE V 7.2(Intel)
运行时库
  • glibc V2.2

C/C++ 应用程序工具的使用

分析和设计
  • Rational Rose
IDE
  • 带有 C/C++ 插件的 IBM WebSphere Studio Application Developer
  • Metrowerks CodeWarrior
低级编辑/编译/调试
  • GNU Compiler Collection V3.0
  • GNU GDB Debugger V5.0
  • Insure++ V5.2
构建
  • GNU make V3.79
  • Automake
打包
  • RPM V4.02
测试
  • DejaGnu
  • TETWare Professional V1.3
性能调优及基准测试
  • GNU Profiler V2.9.1
  • LmBench
源代码管理
  • Rational ClearCase
  • AccuRev/CM V2.6
  • RCS 和 CVS
维护和故障检测
  • Rational ClearQuest Client
  • GNATS V3.113
  • Bugzilla

Java 应用程序工具的使用

  • 分析和设计
  • Rational Rose,ObjectDomain 2.5
IDE
  • Idera Jsync 1.50
  • AnyJ
  • NetBeans Developer 2.1
  • Source Navigator
JVM
  • SUN JVM
  • IBM JVM
编译器
  • Jikes
性能工具
  • Optimizeit
源代码管理
  • Rational ClearCase
维护和故障检测
  • Rational ClearQuest Client

Solaris make 对 GNU make

Solaris make 对 GNU make

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 不完全支持 %。只有第一个 % 才会被替换。

下面是示例:


#Begin Makefile
FOO=abc def
BAR=$(FOO:%=dir1/%.o  dir1/%_cltn.o)
BAR2=$(FOO:%=dir1/%.o) $(FOO:%=dir1/%_cltn.o)

all:
@echo FOO is $(FOO)
@echo BAR is $(BAR)
@echo BAR2 is $(BAR2)

#end Makefile

在 Sun 上,make 命令将打印:


FOO is abc def
BAR is dir1/abc.o dir1/abc_cltn.o dir1/def.o dir1/def_cltn.o
BAR2 is dir1/abc.o dir1/def.o dir1/abc_cltn.o dir1/def_cltn.o

在使用 GNU 的 Linux 上,make 命令将打印:


FOO is abc def

        BAR is dir1/abc.o dir1/%_cltn.o dir1/def.o dir1/%_cltn.o
BAR2 is dir1/abc.o dir1/def.o dir1/abc_cltn.o dir1/def_cltn.o

      

为了解决这个差别带来的问题,您需要按下面所示修改代码:


					
        BAR = $(FOO:%=dir1/%.o)
BAR += $(FOO:%=dir/%_cltn.o)
BAR2 = $(FOO:%=dir/%.o)
BAR2 += ${FOO:%=dir1/%_cltn.o)
			
      

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

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 线程库的对比,以及对接口和定义的

【上篇】
【下篇】

抱歉!评论已关闭.