プロセス間通信(interprocess communication, IPCと呼ばれます)は、プロセスとプロセスの間で通信する方法です。古来より様々な方法が考案されてきました。Wikipediaにもたくさん載ってます。
様々な方法の中には、非常に使いにくい方法があります。というか、本記事でお薦めする3つの方法以外は使うべきではありません。
この記事でお薦めできるプロセス間通信はたった3つです。
- データベース
- ソケット
- http(REST)
これ以外はお薦めできません。
つまり、プロセス間通信として有名な以下の方法はお薦めできません。
- ファイル
- 共有メモリ
- パイプ
- 名前付きパイプ
- メッセージキュー
- セマフォ
- シグナル
お薦めしないものを使っても良い場合
これから、ファイルと共有メモリについてのみ恨みつらみを書き連ねます。興味のない方はお薦めする方法まで飛ばしてください。他の方法も似たようなもので、使用するのが難しいです。
使ってよいと思われるのは、古くから受け継がれていて長年使用している場合です。共有メモリがいかに使いにくいと言っても、試行錯誤を重ね乗り越えた財産があるのであれば幸運です。
Unix草創期は、socket通信、データベースをプロセス間通信に使うのはリソースが多いときに限られました。その時代から、しっかりと時間をかけて設計し、デバグし、様々な事態を想定した作りになっているからこそ生き延びていると思います。数十年を通してデバグしてきたものであれば、信用できる可能性が高いです。
ファイル プロセス間通信で使ってはいけない①
ファイルをプロセス間通信で使ってはいけません。
ファイルを通信に使うことを思いついた!みたいに言う人が数年に一人くらい湧きます。そんなものは江戸時代からみんな思いついていますが、9割以上がその難しさに苦しめられます。
何度でも言いましょう。ファイルをプロセス間通信に使ってはいけません。
簡単そうだからと使いだすと後で後悔します。とんでもない足枷をつけていると考えてください。
ファイルをプロセス間通信に使っている製品を引き継いだトラウマを思い出します。製品化していてバグはないはずなのに、その部分をつつけばつつくだけバグが噴出する事態でした。
開発期間は大幅に超過して怒られるし、毎月の残業時間は100時間を超える月が続き、「ファイルをプロセス間通信にしたやつ死ね!!」と何度も叫びました。
ファイルの何がだめかを語りだすと夜が明けてしまいますが、少し雰囲気を語りましょう。
WindowsとLinuxでファイルは全く異なる
WindowsとLinuxでファイルにアクセスする関数が異なるのはもちろん、仕様がかなり異なります。
つまりファイルを使うとなった時点でポータビリティ(移植容易性)がなくなります。
普通、この時点でファイルを使うという発想はあきらめるものなんですが、それでも押し通す人がいますので、ファイルのうらみつらみを書いていきたいと思います。
以下ファイルはLinuxのファイルとします。
ファイルのロックはfdにかける
fcntl()やflock()でロックすると思いますが、ファイルディスクリプタ(fd)に対してロックをかけます。
ということはopen()しないとロックをかけられません。
これはファイルを作ったり、削除したりというところで排他はできないことを意味します。
ファイルをopen()で作ってからfcntl()の隙間で他のプロセスがopen()-fcntl()という操作が入り込む可能性があります。
どうしてもロックしたいなら、削除しないファイルをあらかじめ用意しておいてそれでロックするなど、奇天烈なことを考え出さないといけません。やめておいた方がよいと思いますよ。
fsync(), sync()しないと書けていないかもしれない
ファイルではwrite()してもメモリ上だけの話でディスクに書かれることはありません。それどころか、close()してもディスクに書かれる保証はありません。ディスクに書かれていることを前提に処理を作るのであれば必ずsync()しなければなりません。
さらに面倒なことに、write(), close()だけであっても、書かれないという保証はなく、書かれているかもしれません。
データベースにはトランザクションの考えがあるので「書かれているかもしれない」「中途半端に書かれているかも」みたいなことは起こりえませんが、ファイルでは「書かれているかもしれない」「中途半端に書かれているかも」が起こりえます。
そういう事態を想定して設計してください。もういやになりませんか?
削除は通るが、削除されたとは限らない
open()されたファイルに対する削除は、(Linuxでは)成功します。削除成功後、open()しているプロセスがclose()したときに、本当に削除されます。
つまり、削除が成功しても削除されたことが保証されるわけではなく時間差が生じる可能性があります。
排他もできないし、writeもdeleteも保証されない世界でどういうソフトウェア設計ができるか!?
このように訳の分からない世界でずっと検討してきて、何がダメか、どういう検討をしなければならないかを認識できる人でないと泳ぎ切るのは至難の業です。
ファイルをプロセス間通信に使ってはいけない、という意味が分かっていただけたでしょうか?
共有メモリ プロセス間通信で使ってはいけない②
ファイルは万人にお薦めできませんが、共有ファイルはUnix草創期の古の方法を知っている人以外におすすめできません。名うての猛者は共有メモリを縦横に使いこなすことができますので、そういう人はお使いください。
共有メモリは切れすぎるナイフです。素人は持たない方がいいですが、真の玄人は持っていいものです。
共有メモリの使いにくいところをあげていきます。
WindowsとLinuxで異なる
Windowsの共有メモリとLinuxの共有メモリは概念が全くことなりますので、全くことなるものと思いましょう。
ポータビリティがありません。
ここからはLinuxの共有メモリです。
プロセス死んでもなくならない
作成したプロセスがいなくなっても共有メモリはなくなりません。
ピンと来ないかもしれませんが、これがソフトウェアの動きに困難さを生じさせます。
例えば、systemdで「プロセスが死んだら再起動」という設定をしているとします。
バグでメモリ不正アクセスなどでプロセスが死んでもプロセスを再起動して動作するという便利仕様です。
さて、共有メモリを作成したプロセスが死んでも、共有メモリはなくなりません。
ということは、プロセスが上がってきた時にすでに作成するはずの共有メモリが存在するかもしれない事態を想定しなければなりません。
これがどれだけ難しいか想像できますか?「メモリ不正アクセスがあって再起動しているなら消せばいいじゃん」と思われるかもしれませんが、本当にメモリ不正アクセスがあったとは限りません。プロセスは本当に死んでいるかどうかを確認する必要があるかもしれないし、残存する共有メモリの後始末をしてから削除しなければならないかもしれません。
どういうものであっても、「プロセス起動、kill」の繰り返しをやられてときに正常に動かなければバグです。共有メモリを使うと決めたなら、しっかりと検討してください。
私は、古の方法を知っている猛者ではありませんので共有メモリは決して使いません。
パイプなども作成と削除の呪いはあるので、そちらを使う人もお気をつけください。
お薦めできる方法
お薦めできる方法は3つ。
- データベース
- ソケット
- http(REST)
これだけです。これら3つの方法は、共有メモリに比べてCPUもメモリも多く使うので昔は忌避されていましたが、現代において問題になることはほぼありません。どんどん使っていきましょう。
データベース お薦めできる方法①
感覚的にはファイルとデータベースは似たようなものに感じる人もいるかもしれませんが、全然違います。
排他、作成、削除の仕様が明確です。ファイルとは全く異なります。
アクセスの方法が確立されていて、ちゃんとした人が設計すれば、そう悪いものにはなりません。
プロセス間通信とは全く異なる文脈で、とても精通している人が多数いるので、大丈夫ですね。
データ設計、CRUD設計などデータベースの普通の設計を息を吸うようにやっていただければ何の問題もありません。
データベースに詳しい人がいない場合はhttpを使いましょう。
ソケット お薦めできる方法②
ソケットです。単純なインタフェースであればソケット、複雑なものはhttp(REST)にすればよいかと思います。
欠点はないのですが、独自インタフェースをしっかり設計する必要があるので、2010年代くらいから「ソケットよりhttp使おうよ」という空気です。したがって情報少ないです。下記の本が分かりやすくてよいと思います。
現代のITエンジニアはhttp(REST)にしましょう。
http(REST) お薦めできる方法③
一般的なRESTがどういうものかを知っておく必要はあり、httpサーバを作る必要もありますが、ライブラリを駆使できれば、jsonを送受信すればよいです。ぜひ、http(REST)を使っていきましょう。
下記の本のように、通信する方法のことを考えず、分かりやすい通信方法、APIの設計を考えていきましょう。
まとめ
プロセス間通信について論じました。
データベース、http(REST)がお薦めです。間違ってもファイルをプロセス間通信に使うのだけはやめてください。
コメント