プロセス間通信
IPCとは
プロセス実行中に他のプロセスと通信を行うための機構を考えます。通信とい うことでは2つの方法を挙げることができます。
- ローカルマシン内(もちろん動いているカーネルは1つという前提)で動作しているプロセス間での通信。
- ネットワークを経由して通信するネットワーク通信。
本章では前者のローカルマシン内での通信を取り上げます。もちろんローカル
マシン内で動作するプロセス同士でもネットワーク経由と同じようにTCP/IPを
使って通信できますが、本章のプロセス間通信の説明からはTCP/IPは除いてお
きます。
ここでのプロセス通信(InterProcess Communication / IPC )とは、名前つき
パイプ(named pipe)、pipe、UNIXドメインソケット(ローカルIPC)、セマフォー
やあるいはシェアードメモリなどを指しています。これらにより高速にプロセ
ス間でデータをやりとりするためのメカニズムと捉えます。Linuxでは、
System Vで使われていたプロセス間通信の機能、4.2BSDで採り入れられたソケッ
ト、名前つきパイプが使えます。
- 補足
- 単一カーネル内ではIPのルーティング機能などの不必要な情報の付加や処理を
行わずに済むので、その点から「高速にデータをやりとり」と書いていますが、 これは定性的な観点からいっているものです。実際のパフォーマンステストに よるものではありません。
System V IPC
プロセス間通信の機能というと、System Vで使われていたプロセス間通信の機 能(System V IPC)のセマフォー(semaphore)、メッセージキュー(message queues)、シェアードメモリ(shared memory)が有名です。
- 補足
セマフォーとは、元々は列車運行で列車が単線の区間に入ることを許されてい るか、あるいは待つのかを示す信号機なのだそうです。
ソフトウェアの世界では、複数のプロセスが同じ資源の競合を避けるために、
使われる機能で、たとえば複数のプロセスが同時にファイルを書き込みするタ
イミングが同じになってしまわないように、セマフォを使って待ちます。セマ
フォは高速に処理できるので、たとえばデータベースのようなアプリケーショ
ンでの処理のロックなどに使われています。しかし、同様な処理はmmapを使っ
ても可能ですので、今日ではありまり価値があるとはいえません。むしろ、互
換性のために残していると考えた方が良いでしょう。
semget (2) - セマフォーの獲得 semctl (2) - セマフォーの制御 semop (2) - セマフォーの設定
メッセージキューは古いアプリケーションを動かすための互換性のために残さ
れているようなもと考えても良いレベルになって来ています。今日では新規の
プログラムに使うような場面を見かけたことがありません。最近ではメッセー
ジキューを使う所を名前つきパイプで済ますことができます。シェアードメモ
リもmmapが使える今日ではあまり特別な価値はありません。現在もあるのは、
互換性のためにあると考えてかまわないでしょう。
名前つきパイプ
その前にパイプを説明しましょう。コマンドラインでのシェルが持つパイプは、 前のコマンドの標準出力を後ろのコマンドの標準入力にするというものです。 使い勝手は、まるっきりファイルです。
現在のディレクトリにあるファイル数をカウントする % ls | wc
このようなプロセス間で一方向に書き出し、読み込みをするプログラムを書く
時は、UNIXの初期からあるシステムコールとしてpipe(2)を使って実現します。
pipe(2) は、プロセス中で2つの要素を持つファイルデスクリプタ配列に対し
て、1つは書き込み、もう1つは読み込みのディスクリプタを与えるというもの
です。このペアを作っておき、プロセスがフォークすると、1つのプロセス側
は書き込み、もう一つのプロセス側は読み込みができるようになります。もち
ろん一方向にしかデータは流れません。
さて、名前つきパイプは、それまでのプロセスがフォークして資源を継承する
しかできない一方向に流れるパイプとは違い、2つの完全に独立に存在してい
るプロセス間でデータをやり取りするために作られたものです。
FIFO(First-In-First-Out)である名前つきパイプを作ります。これはファイル
のように名前でアクセスできるFIFOの性質を持った双方向パイプを作ります。
まずコマンド mkfifo で名前つきパイプのファイルを作ります。ファイルといっ
てもアクセスのために名前だけあって実態はパイプです。 ls -l で見ると先
頭がpがあるので、名前つきパイプであることがわかります。あとls -Fとして
みると、npの後ろに"|"がついて出力されます。これは名前つきパイプ(FIFO)
のファイルであるという意味です。
$ mkfifo np $ ls -l np prw-r--r-- 1 hironobu hironobu 0 Dec 16 21:58 np $ ls -F np np|
この状態で2つのshellウインドウを開いてみてください。一つはnpを読む、も
う一つではnpに書き込むことをしてみます。
shellウインドウ1 $ cat np abcdef <-- 表示される 123456 <-- 表示される $ <-- 終了する shellウインドウ2 $ cat > np abcdef <--入力 123456 <--入力 ^D <-- ^Dで終了 $
ここでは判りやすいように一方向にデータを送っている例を出していますが、
このようにファイル名でアクセスするようにしてプロセス間の通信が出来ると
いうのは、実にUNIXらしいやり方です。プログラム中から名前つきパイプを作
る時はユーザ関数 mkfifo(3)で作れます。
UNIXドメインソケット (ローカルIPC)
これはTCP/IPネットワーク接続の機能のインタフェースと同じものを用意して、 しかし、データはローカルなプロセス間通信に使おうというものです。元々は UNIXドメインソケットと呼んでいたのですが、Posix ではUNIXに依存しないの でローカルIPC という呼び方をしています。しかし、その呼ばれ方はあまりに も知られていないので、カッコつきで(UNIXドメインソケット)と並べて置きま した。
- 補足
- POSIXでは「ローカルIPC」という呼び方をしますが、UNIX流では
UNIXドメインソケットと呼びます。
socket(2)、bind(2)、accept(2)のようにTCP/IPの通信を行うやり方は、今ま
でのUNIXとはセマンティクス(意味的なもの)が違います。UNIXが、すべてを
名前つきパイプのように名前空間でアクセスしようとするのに対して、TCP/IP
のような通信系のやり方は、一々、IPアドレスやポート番号を指定しなければ
いけません。
int sockfd; struct sockaddr_un addr; ... sockfd=socket(AF_LOCAL, SOCKET_STREAM,0); ... addr.sun_family = AF_LOCAL; strcpy(addr.sun_path, "/tmp/mysocket"); bind(sockfd,&addr,SUN_LEN(addr)); ....
ソケットをAF_LOCALで作成し、sun_familyをAF_LOCALに指定し、sun_path の 部分にファイルパスを書きバインドすると、そのパスにファイルに見えるソケッ トが出来ます。こうすれば、あとのデータの送信/受信に関係するプログラム の構造はTCP/IPと同じに作れます。その面ではインターネット経由でアクセス するプログラムとローカルにアクセスするプログラムが同じ構造で作れる利点 があります。
$ ls -lF /dev/log srw-rw-rw- 1 root root 0 Aug 23 14:13 /dev/log=
これはログデーモンがオープンしているソケットです。しかしながら、パス名
で見えていても、ファイルではないので、通常のファイルを扱うコマンドでア
クセスしてもエラーになります。
かな漢字サーバWnnのソケットをcatで見てみる $ ls -lF /tmp/jd_sockV4 srwxr-xr-x 1 wnn nogroup 0 Sep 21 13:48 /tmp/jd_sockV4= $ cat /tmp/jd_sockV4 cat: /tmp/jd_sockV4: No such device or address
これらはIPスタックを経由しません。IPは複数の独立したホストがあり、ネッ
トワーク構築された世界をIPパケットが中継されていくモデルです。そのため
にIPパケットを処理するためには、そのための処理がなされます。一方、UNIX
ドメインソケットには、そんな付加する情報をつけたり処理したりする必要は
ありません。よって高速に処理することが可能になります。