Solaris OS 与 Linux 的比较(适用于应用程序开发者)

 
作者 Max Bruning,2006 年 6 月  
内容
 
简介
系统调用和库
结论
 

许多开发者正在编写在 Linux 操作系统下运行的应用程序。随着 Solaris 10 OS 中引入许多新增功能,以及随着 Sun 最近对于在基于 AMD 和 Intel 处理器的计算机上支持 Solaris OS 的格外重视,开发者对于能够在 Solaris 平台上开发应用程序越来越感兴趣了。本文分析了这两种操作系统的开发环境之间的相似和不同之处。负责将应用程序从 Linux 移植到 Solaris OS 的人员,或者之前已具有 Linux 经验而想要学习在 Solaris OS 中进行开发的程序员,将会从本文中获益。

在本文中,术语 "Solaris" 指 Solaris 10 OS(和 OpenSolaris),而 "Linux" 指 Linux 2.6。所涵盖的许多详细信息也适用于 Solaris 和 Linux 的早期版本。Linux 分发版本是指通用分发版本,而示例在 SuSe 9.1 中进行了测试。另外,本文集中讲述使用 C 编程语言编写的应用程序,而使用 C++ 时的情况应该与使用 C 时类似。由于基于 Java 技术的应用程序不应有特定于 Linux 或 Solaris OS 的函数调用,因此可以按原样移植。

简介

本文讨论应用程序程序员和分析员能够看到的有关 Solaris OS 与 Linux 的相似和不同之处。本文并不代表对不同之处的全部说明,也不是用于说明一种 OS 优于另一种。相反,本文旨在帮助那些熟悉其中一种 OS 的开发者能够尽可能快地使用另一种 OS。

与 POSIX 兼容且不含任何特定于 Solaris OS 或 Linux 的系统调用或库函数的简单应用程序应可在两种 OS 之间移植,而无需进行更改。您应该能够编写您的应用程序、针对 Solaris OS 或 Linux 进行编译,然后针对另一种 OS 进行简单重新编译,这样它应该就能正常使用。这两种 OS 中的大多数系统调用和库例程都属于这种类型。

Linux 中的许多系统调用在 Solaris OS 中作为库函数存在,反之亦然。例如,sched_setscheduler() 在 Linux 中为一个系统调用,而在 Solaris OS 中为一个调用 priocntl(2) 系统调用的库函数。priocntl(2) 系统调用在 Linux 中不存在,但 Linux 不支持除分时和实时以外的多个调度程序。本文接下来的一节将系统调用按功能分成多个部分逐一进行介绍,并对比每种 OS 提供的功能。

来自 Linux 领域的大多数应用程序和工具包都可直接编译和运行,而无需进行更改。这包括 gcc、emacs、MySQL、perl 以及许多其他应用程序和工具包。http://www.sunfreeware.com 中提供了许多软件包的预编译二进制代码。

所提供的有关比较 Linux 和 Solaris OS 的文章为数并不多,但大多数这样的文章都是对这两种操作系统的早期版本进行比较。您可以通过在 Web 中搜索 "Linux and Solaris comparison"(Linux 与 Solaris 的比较)来找到这些文章。请参见有关 Solaris OS 和 Linux 的《Seal Rock Research White Paper》(pdf),该文中涵盖了 Solaris 10 OS 和 2.6 Linux。"Migrating Solaris Applications to Linux" 是讨论有关从 Solaris OS 移植到 Linux 问题的若干页的开始部分。

Solaris OS 和 Linux 之间以及 Linux 的不同分发版本之间存在着各种管理差异。Solaris 10 OS 中引入了“服务管理框架”(Service Management Framework, SMF),较之以前的 Solaris 版本,这是一个很大的变化。本文将不涵盖系统管理方面的差异,但某些对开发者有影响的部分除外。

系统调用和库

Linux 中存在的大多数系统调用和库在 Solaris OS 中也同样存在。本节将介绍在两种系统间存在差异的系统调用和库例程。系统调用和库例程的分类如下:

Solaris OS 在 /usr/include/sys/syscall.h 中保存着系统调用的列表。Linux 在 /usr/include/asm/unistd.h 中维护着相同的信息。(请注意,Linux 和 Solaris OS 都有 unistd.hsyscall.h 文件,并且在某些情况下,这些文件的内容相同。)

Solaris OS 和 Linux 的 /usr/share/man/man2 中提供了系统调用的文档。(Solaris OS 具有一个从 /usr/man 到相同位置的符号链接。)库例程记录在各种手册部分。有关 Linux 和 Solaris OS 的库部分的概述,请参见 man intro.3。请注意,与 Linux 相比,Solaris OS 对库例程的划分更细。例如,在 Solaris OS 中,aio_read() 记录在 aio_read(3RT) 中,而在 Linux 中,它记录在 aio_read(3) 中。其结果是,使用 aio_read() 在 Solaris OS 中编译程序时,您必须通过 -lrt 及 compilation/link 命令包括实时库,而在 Linux 中则不需要。

Linux 和 Solaris OS 都附带提供了 200 多种不同的库,这些库中定义了 50,000 多个函数。

下表列出了 Linux 和 Solaris OS 中的某些库。请注意,这并不代表完整的列表。另请注意,其中的某些库必须独立于标准系统安装而单独下载和安装。

表 1:Linux 和 Solaris OS 中的某些库
 
 
Solaris OS
Linux
说明
libc
libc
标准 C 库(POSIX、SysV、ANSI,等),请参见 Solaris OS 中的 man libc。
libucb
libc
UCB(柏克莱加州大学,University California Berkeley)兼容性库
libmalloc
libc
有多种不同的 malloc 库;缺省库位于 libc 中。
libsocket
libc
套接字库(在 Linux 中,套接字位于 libc 中)。
libxnet
libc
X/Open 联网库
libresolv
libresolv
DNS 例程(在 Solaris OS 中为 inet_* 例程)
libnsl
libnsl/libc
网络服务库(linux-nis/nis+ 例程)
librpc
librpc
RPC 函数
libslp
libslp
服务定位协议
libsasl
libsasl
简单验证和安全层
libaio
libaio
异步 I/O 库
libdoor
 
门支持(door_create()door_return(),等)
librt
librt
POSIX 实时库
libcfgadm
 
配置管理库
libcontract
 
约定管理库(请参见 Solaris OS 中的 man contract.4
libcpc
 
CPU 性能计数器库(在 Linux 中,可能需要安装内核模块)
libdat
 
直接访问传输库(请参见 http://www.datcollaborative.org
libelf
libelf
ELF 支持库
libm
libm
数学库

以下各节将更加详细地介绍某些系统调用和库。我们将集中讲述它们在系统之间的不同之处。

套接字和联网

对于大多数套接字和联网代码而言,只需针对所使用的 OS 对其进行重新编译,所生成的可执行代码应该就能正常使用。本节对通常在 Solaris OS 和 Linux 中使用的与网络有关的系统调用和库例程进行了比较。

socket()

除了 AF_UNIXAF_INETAF_INET6 域参数以外,socket() 例程在 Solaris OS 和 Linux 中还具有其他值。在 Solaris OS 中,AF_NCA 域用于指定网络高速缓存和加速器(请参见 nca(1))以便与套接字一起使用。大多数地址族(域)都在 Linux 和 Solaris OS 中同时存在。注意:有关可能的地址族,请参见 Solaris OS 中的 /usr/include/sys/socket.h 以及 /usr/include/linux/socket.h。但可能需要下载或编写代码才能支持某些域。

Linux 将若干其他域记录在 socket(2) 手册页中。Linux 中记录的其他域包括:

  • AF_IPX-Novell IPX 协议(可能仅用于 SuSe)。
  • AF_NETLINK-内核/用户接口设备,允许用户访问内核模块。注意:在 Solaris OS 和 Linux 中还存在一些其他方法用于进行这项工作。
  • AF_X25-X25 协议。在 Solaris OS 中,此域包含在 Solstice X.25 产品中。
  • AF_AX25-业余无线电 AX.25 协议。
  • AF_ATMPVC-基于 ATM 的永久虚拟电路 (Permanent Virtual Circuits over ATM)。
  • AF_APPLETALK-请参见 Linux 中的 man ddp。它在 Solaris OS 中也存在,但未记录。
  • AF_PACKET-请参见 Linux 中的 man packet.7。原始包接口。在 Solaris OS 中,请打开 NIC 设备并使用 getmsg(2)/putmsg(2) 来通过 DLPI 接收/发送原始包。(有关 DLPI 的详细信息,请参见《Data Link Provider Interface (DLPI), Version 2》)。
bind()

Linux 手册页 (man bind.2) 包含一些有关不同地址族(AF_INETAF_UNIX 除外)的信息。Solaris 手册页为 man bind.3socket

listen()

在 Linux 和 Solaris OS 中,backlog 参数(listen() 的第二个参数)指正等待被接受的已建立连接的队列长度。Linux 手册页中是这么说的,而 Solaris 手册页中仅是指“待处理连接的队列”。

accept()

Linux 支持三种基于连接的套接字类型:SOCK_STREAMSOCK_SEQPACKETSOCK_RDM,而 Solaris OS 只记录了 SOCK_STREAM。Linux 实现不继承某些套接字标志。这可能与其他实现不同。

connect()

Linux 手册页 (man connect.2) 记录了 SOCK_SEQPACKET,而 Solaris OS 中不记录。Linux 通过连接到 struct sockaddrsa_family 设置为 AF_UNSPEC 的某个地址来中断无连接套接字和 connect() 之间的关联。此行为不会记录在 Solaris OS 中。

send()/recv()

如在其他 socket 库函数中的情况那样,它们在不同系统中的行为几乎完全相同。Linux 在手册页中包含一些其他 flags 参数文档。

shutdown()

在 Solaris OS 和 Linux 之间没有显著的差异。

联网示例

查看一个其中存在着某些差异的应用程序可能会很有用。tracedump 程序使用包捕获库 (libpcap) 在用户级读取以太网包。用于读取原始以太网的代码在 Solaris OS 和 Linux 之间完全不同。(也可使用 libpcap 来检查与其他系统(例如,FreeBSD、HP-UX 和 AIX)间的差异。)libpcap 的可用代码位于 pcap-linux.cpcap-dlpi.c 中。DLPI 代码用于 Solaris、HP-UX、AIX 和其他操作系统。Linux 提供了一种用于通过标准 socket 调用读取原始套接字包的机制。Solaris OS 使用 getmsg(2)putmsg(2) 调用来接收和发送 DLPI 包。

以下代码演示了一种在 Solaris OS 中对网络接口执行用户级包捕获的方法。在这段代码之后,列出了在 Linux 中采用的类似代码。这段代码提取自 libpcap 库(此处进行了大量删减)。

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int
main(int argc, char *argv[])
{
    register char *cp;
    int fd;
    dl_info_ack_t *infop;
    union DL_primitives dlp;
    dl_info_req_t inforeq;
      dl_bind_req_t    bindreq;
    dl_attach_req_t attachreq;
    dl_promiscon_req_t promisconreq;
    struct    strbuf    ctl, data;
    int    flags;
    char buffer[8192];
    dl_error_ack_t *edlp;

    fd = open(argv[1], O_RDWR);  /* for instance, /dev/elxl0 */

    /* attach to a specific interface */
    attachreq.dl_primitive = DL_ATTACH_REQ;
    attachreq.dl_ppa = 0;  /* assume we want /dev/xxx0 */
    ctl.maxlen = 0;
    ctl.len = sizeof(attachreq);
    ctl.buf = (char *)&attachreq;
    flags = 0;
    /* send attach req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get ok ack, may contain error */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    memset((char *)&bindreq, 0, sizeof(bindreq));
    /* the following bind might not need to be done */
    bindreq.dl_primitive = DL_BIND_REQ;
    bindreq.dl_sap = 0; 
    bindreq.dl_max_conind = 1;
    bindreq.dl_service_mode = DL_CLDLS;
    bindreq.dl_conn_mgmt = 0;
    bindreq.dl_xidtest_flg = 0;
    ctl.maxlen = 0;
    ctl.len = sizeof(bindreq);
    ctl.buf = (char *)&bindreq;
    flags = 0;
    /* send bind req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get bind ack */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    promisconreq.dl_primitive = DL_PROMISCON_REQ;
    promisconreq.dl_level = DL_PROMISC_PHYS;
    ctl.maxlen = 0;
    ctl.len = sizeof(promisconreq);
    ctl.buf = (char *)&promisconreq;
    flags = 0;
    /* send promiscuous on req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get get ok ack */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    promisconreq.dl_primitive = DL_PROMISCON_REQ;
    promisconreq.dl_level = DL_PROMISC_SAP;
    ctl.maxlen = 0;
    ctl.len = sizeof(promisconreq);
    ctl.buf = (char *)&promisconreq;
    flags = 0;
    /* send promiscuous on req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get get ok ack */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    /* read and echo to stdout whatever comes to us */
    while (1) {
      data.buf = buffer;
      data.maxlen = sizeof(buffer);
      data.len = 0;
      ctl.buf = (char *) &dlp;
      ctl.maxlen = sizeof(dlp);
      ctl.len = 0;
      flags = 0;
      getmsg(fd, &ctl, &data, &flags);
      write(1, "\nCTL:\n", 6);
      write(1, ctl.buf, ctl.