| 作者 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.h 和 syscall.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 中的某些库。请注意,这并不代表完整的列表。另请注意,其中的某些库必须独立于标准系统安装而单独下载和安装。
 |
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 |
|
|
libelf |
libelf |
ELF 支持库 |
libm |
libm |
数学库 |
以下各节将更加详细地介绍某些系统调用和库。我们将集中讲述它们在系统之间的不同之处。
套接字和联网
对于大多数套接字和联网代码而言,只需针对所使用的 OS 对其进行重新编译,所生成的可执行代码应该就能正常使用。本节对通常在 Solaris OS 和 Linux 中使用的与网络有关的系统调用和库例程进行了比较。
socket()
除了 AF_UNIX、AF_INET 和 AF_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_INET 和 AF_UNIX 除外)的信息。Solaris 手册页为 man bind.3socket。
listen()
在 Linux 和 Solaris OS 中,backlog 参数(listen() 的第二个参数)指正等待被接受的已建立连接的队列长度。Linux 手册页中是这么说的,而 Solaris 手册页中仅是指“待处理连接的队列”。
accept()
Linux 支持三种基于连接的套接字类型:SOCK_STREAM、SOCK_SEQPACKET 和 SOCK_RDM,而 Solaris OS 只记录了 SOCK_STREAM。Linux 实现不继承某些套接字标志。这可能与其他实现不同。
connect()
Linux 手册页 (man connect.2) 记录了 SOCK_SEQPACKET,而 Solaris OS 中不记录。Linux 通过连接到 struct sockaddr 中 sa_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.c 和 pcap-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. | |