UNIX間でのアプリケーション移植の基礎知識
UNIXを元にした OS は数多くあり、単純に一つのプログラムを作ったからといってすべての UNIX 上で簡単に動作するわけではありません。Solaris, HP-UX, AIX, Linux, FreeBSD など、それぞれの OS に合わせたプログラムを作らなければなりません。そこで、ここでは UNIX間で移植性のあるプログラムを作るにはどんな点に注意しなければならないかまとめてみます。
はじめに
一般的な移植上の問題として以下のことが上げられます。
ハードウェアの違い(ハードウェアに依存するコード)
オペレーティングシステムの違い(OSに依存するコード)
構築環境の違い(環境に依存するコード)
ここでは、これらの問題を取り上げどういった点に注意が必要なのかを簡単に記述します。
ハードウェアに依存するコード
移植性を考慮したプログラムを作る上でもっとも難しいのは、特別なハードウェアを含むアプリケーションを作成する場合です。この節では、以下の3種類のハードウェア依存の問題について考えます。
独自仕様なインターフェース
バイトオーダー (エンディアン)
アライメント
● 独自仕様なインターフェースを持つものの移植性を高める
UNIX アプリケーションには、データオペレーションをより高速化するために固有の CPU 命令(アセンブラ)を使用することによって、独自なインターフェースを使用するものがあります。これらのインターフェースは、システムモニタやイメージフィルタ、デバイスドライバなどでしばしば書かれています。ほとんどの場合、これらは以下の方法のうちのいずれかにより、インターフェースの移植性を高めることができます。
機能を既存の標準に割り当てる | 独自で機能を実現しているものが、POSIXなど標準的なもので実現されている場合、それらを標準のものに置き換えることで移植性が高まります。 |
ルーチンをカプセル化したラッパー関数を作成する | どうしても、高速化が必要なところやハードウェアに直接アクセスしなければならないところがある場合、それらの部分をカプセル化するようなラッパー関数を作成する。システムに依存する関数を単純に置き換えることで、他の部分を変更せずにアプリケーションが作成できるようにすることで移植性が高まります。 |
ハードウェア依存性のないアプリケーションに書き直す | ハードウェアに依存しない記述が可能なら、独特な記述をするのではなくハードウェアに依存しない記述をすることで移植性が高まります。 |
● バイトオーダーに注意して移植性を高める
データ構造体内の個々のバイトの順序(バイトオーダー)は、移植性のあるアプリケーションを作成する上で問題となることがあります。バイトオーダーの扱いで注意すべき問題は主に次の 2 点です。
プログラムを稼動するプロセッサのバイトオーダーについての誤った仮定をする場合
異なったバイトオーダーをサポートする環境から構造化されたデータをエクスポートする場合
バイトオーダーには、ビックエンディアンとリトルエンディアンの二種類があります。
ビックエンディアンプロセッサ(SPARC, PowerPC)は、最上位バイト(MSB)を最下位のバイトアドレス(最下位バイト)にストアするという順序化を行います。また、リトルエンディアンプロセッサ(x86, Alpha)は、最下位バイト(LSB)を最下位のバイトアドレス(最下位バイト)にストアするという順序化を行います。
シングルバイト(8 ビット)でアドレスする場合、ビックエンディアンとリトルエンディアンのマシンでは、同じ方法でアドレスを行うので問題はありません。しかし、マルチバイト(ハーフワード、ワード、ダブルワード)でアドレスする場合は、バイトオーダーは異なります。
アドレス | +0 |
+1 |
+2 |
+3 |
リトルエンディアン | 0x34 |
0x12 |
0xCD |
0xAB |
ビッグエンディアン | 0xAB |
0xCD |
0x12 |
0x34 |
バイトオーダーによる格納方法の違い 4bytes幅のデータ「0xABCD1234」を、メモリ中へ格納した場合。最下位バイトから順に格納するのがリトルエンディアン、最上位バイトから順に格納するのがビッグエンディアン。 |
ネットワークを使った通信では、バイトオーダーの異なるマシンやシステム同士で通信する可能性があります。このとき、バイトオーダーが異なっていると正しく通信できません。このような不都合が起こらないように、ネットワークで通信をする場合、あらかじめバイトオーダーを決めてあります。これをネットワークバイトオーダーといいます。たとえばTCP/IPプロトコルでは、ネットワークバイトオーダーはMSBから先に送るビッグエンディアンとなっています。
● アライメントに注意して移植性を高める
double と long double の浮動小数点変数のアライメントの違いがアライメントの違いがポーティングの問題を引き起こすことがあります。double変数について、SPARC プロセッサでは 8 バイト、x86 プロセッサでは 4 バイトのアライメントが行われます。
struct TestA { char c; short s; double d; }; struct TestB { short s; double d; char c; };
sizeof 関数でサイズを調べてみると、それぞれの構造体がパディングにより
x86 | TestA = 12, TestB = 16 |
SPARC | TestA = 16, TestB = 24 |
となることがわかります。ちなみに、double 変数を使用しない場合は以下のようになります。
struct TestA { char c; short s; int i; }; struct TestB { short s; int i; char c; };
x86 | TestA = 8, TestB = 12 |
SPARC | TestA = 8, TestB = 12 |
これら、アライメントと構造体パディングは、コンパイラによって管理されているので、コンパイラによってはこれらの差異を吸収してくれるコンパイラオプションを持っているものもあります。 しかし、アプリケーションの実行速度がひどく低下することがあります。
OSに依存するコード
オペレーティング・システム間での移植性の問題の多くは、それぞれが標準と準拠していないことから発生しています。 SVID、POSIX、ANSI-C、X11、TCP/IP、X/OPEN などの標準の準拠度合いを見てみましょう。
SVID | POSIX | ANSI-C | X11 | TCP/IP | X/OPEN | 64 ビット | その他 | |
Solaris 8 | SVID3 SVR4 ABI |
IEEE1003.1 IEEE1003.1b IEEE1003.1c IEEE1003.2 |
○ | X11R6 OpenGL |
NFS3 |
UNIX95 UNIX98 CDE1.4 |
○ | JVM,JDK WebNFS DirectIO,AIO Neo/CORBA Kodak |
Solaris 2.6/7 | SVID3 SVR4 ABI |
IEEE1003.1 IEEE1003.1b IEEE1003.1c IEEE1003.2 |
○ | X11R6 OpenGL |
NFS3 |
UNIX95 UNIX98 CDE1.2 |
○ | JVM,JDK WebNFS DirectIO,AIO Neo/CORBA Kodak |
HPUX 10.20 | SVID3 ベースの カーネル Level1API |
IEEE1003.1 IEEE1003.2 IEEE1003.1b IEEE1003.1c |
X3.159 | X11R6 OSF/Motif1.2.5 |
NFS Sockets |
UNIX95 CDE1.0 |
× | DCE1.0 |
HPUX 11.0 | SVID3 | IEEE1387.2 | X3.159 | X11R6 | NFS3 IPv6 |
UNIX95 base95 CDE1.0 |
○ LP64 |
KernelJVM JIT,JDK WebAdmin JFS VLM(4TB) |
IBM-AIX 4.2 | SVID2 ベース |
IEEE1003.1 IEEE1003.2 |
X3.159 | X11R5 OSF/Motif1.2 SGI-GL3.3 |
NFS3 Sockets |
UNIX95 base95 CDE1.0 |
× | Monitor、API Scientific,Lib Parallel,prog OLTP Clusters/HA |
IBM-AIX 4.3 | SVID2 ベース |
IEEE1003.1 IEEE1003.2 |
X3.159 | X11R6 OpenGL,1.1 PHIGS,1.0 |
IPv6 LDAP SSLv3 |
UNIX98 base95 CDE1.0 |
○ | JDK,JIT WebAdmin DirectIO |
SGI-IRIX 6.2 | Subset of SVID3 |
IEEE1003.1 IEEE1003.1b IEEE1003.1c IEEE1003.2 |
○ | X11R6 OpenGL |
NFS3 Sockets PPP |
UNIX95 Base95 |
○ | DirectIO MagicUI CosmoVRML DCE1.1 |
DEC OSF/1 | SVID2 ベースの カーネル |
IEEE1003.1 | X3.159 | XPG3 | NFS Sockets |
** | ○ | DLI |
DigitalUnix 4.0C |
SVID2 ベース SVID3 カーネル |
IEEE1003.1 IEEE1003.1b IEEE1003.1c IEEE1003.2 |
○ | X11R6 OSF/Motif1.0 |
NFS3 Sockets PPP |
UNIX95 CDE1.0 |
○ LP64 |
MFS DataMining Dataware exceeds C2 IPMulticast |
表のように、それぞれ準拠しているように思えますが、Solaris は前身である SunOS 4 (BSD) の多くの機能を融合してあるため制限の多い System V ファイルシステムや関連ユーティリティがありません。 HP-UX も、多くの機能やコマンドが BSD系の UNIX に近いものがあります。
標準への準拠以外にも OS に依存することはあります。特に、UNIX ソケットを使ったクライアント/サーバ・プログラムのコーディングの際、telnet セッションで Ctrl-Cをタイプしたり、データベースのフロントエンドが SQL クエリ処理の終了を指示するなど帯域外 (Out-Of-Band) データの通信が必要なことがあります。このとき、プロセスはカーネルに対して ioctl() か fcntl() を呼び出し、OOB を受け取る準備ができていることを示す必要があります。
#if defined(hpux) ioctl(fd, FIOSSAIOOWN, getpid()); /* HP-UX システムの場合 */ #elif defined(sco) ioctl(fd, SIOCSPGRP, getpid()); /* SCO システムの場合 */ #else fcntl(fd, F_SETOWN, getpid()); /* 他の UNIX の場合 */ #endif
また、シグナルに関する振る舞いは、BSD系と System V 系で異なります。
4.3BSD |
|
SVR4 |
|
シグナルに関しては、POSIXの sigaction()関数を使用することを推奨します。この関数は、望ましい振舞いを実現するように、相当に柔軟性に富んでいるので非常に推奨されてます。 POSIX インタフェースへの移行は、コードの移植性を高める正しい方向です。
異なるデータモデルによる、問題についても認識しておく必要があります。
32ビットのUNIXコンピューティングと64ビットのUNIXコンピューティングとでは、使用するデータモデルが異なります。32ビットのUNIXコンピューティングで使用されるデータモデルはILP32と呼ばれ、 整数 、 long整数 、およびポインタはすべて32ビットのデータタイプになります。64ビットのUNIXコンピューティングで使用されるデータモデルはLP64と呼ばれ、 long整数 およびポインタは64ビットのデータタイプになり、整数 は32ビットのデータタイプのままになります。既存のCコードおよびC++コードの中には、 整数、long整数、およびポインタを同じサイズであると想定して処理を行っているものがあります。LP64ではこの想定が正しくないため、実行モジュールによっては、64ビット用にコンパイルすると問題が生じる場合があります。
32 ビットアプリケーションが 64 ビットアプリケーションとデータ交換が必要な場合、データ型の違いによる問題が起きるかも知れません。
環境に依存するコード
それぞれ UNIX系の OS のあいだで、環境に依存する部分でも様々なアプリケーション移植の問題が発生します。特にヘッダーファイル(インクルードファイル)の不一致によって発生します。ヘッダーファイルの置かれている物理的な場所の不一致や、微妙にヘッダーファイル名が異なる場合があります。
#ifdef AIX #include <sys/limits.h> #endif #ifdef SOLARIS #include <limits.h> #endif #ifdef HP #include <strings.h> #endif #ifdef SOLARIS #include <string.h> #endif
これは、ヘッダーファイルだけでなく、使用する関数を含むライブラリーが異なる場合があります。たとえば、ソケットに関する関数は、libc に含まれていることが多かったのですが Solaris などでは libsocket、libnsl に含まれています。また、make など開発に必要なコマンドが OS によって異なる場所にあるので、開発環境を整備する際に注意が必要になります。これらのことを考慮して、開発環境を整える必要があります。
ここであげたものだけが、移植する際に注意すべき点のすべてではありません。ヘッダーファイル内に定義してあるデータなど、まだ細かい点で異なる部分はあります。移植作業をする人のちょっとした Tips として利用してください。