IPCとかパイプとかソケットとかgRPCとか

通信に関する話。

手元にあるデータを解析し、その結果をいい感じのグラフに出力したかっただけなのだが、ElectronやQtを使うときにIPCが出てきたので勉強した。いろいろ考えたことを忘れないうちにまとめておく。

パイプ

パイプはIPCに使われる。パイプはキューである。OS管理下にあり、プロセスと無関係に存在できる。ブロッキング・ノンブロッキングを選択でき、通信が失敗する条件が豊富で、送信に失敗するとSIGPIPEが飛ぶことがある。パイプはファイルディスクリプタ(FD)を使ってRead/Writeして送受信する。ソケット通信もFDを使って行うので、とてもよく似ている。ブロックされたくなければ、selectしてポーリングする。

標準入出力はパイプである。通常、各プロセスのFDの0,1,2はstdin, stdout, stderrに割り当てられる。forkするとプロセスがまるごと複製され、FDはdupされる。同じFDの番号を持ち、同じデバイスを参照していても、OS内部では別物である。参照カウンタが増加するので、新しいパイプを作った後にforkしたら、両方のプロセスが1本のパイプで送受信できる状態になっている。基本的に一方向通信に使うものなので、誤って送受信しないよう、使わない出口をcloseする。。

UNIX/Linuxの場合、使い捨ての無名パイプとファイルシステムに永続化される名前付きパイプがある。Windowsの場合はCreateNamedPipeで名前つきパイプを作成し、無名パイプはランダムな名前つきパイプとして作られる。

IPCの手段

パイプを使うと一方向または双方向にデータを流せる。ソケットはこれを標準化・広域化したイメージ。ミューテックスと共有メモリを使う方法があって、これが多分一番高速なIPCの手段。

パイプからソケットへ

キューを使って相手側に一方的にデータを送り付ける通信方式はPrimitive。でもアプリケーションは通信の失敗やタイムアウトが知りたかったり、高速化のために多重化したいはず。

そう見てみると、プロトコルが少しずつ進歩してきた様子が見えてくる。

Unix Domain Socketは便利なNamed Pipeみたいなもの(unix socket over TCPとかあるので本当はもっとすごいはず)

UDP/IPはコネクションレスの特殊なプロトコルでデータを送るもの。

TCP/IPは信頼性を高めたプロトコル。受信者はACKを返すので、送信に成功したかどうかが分かる。IPの通信経路は1本だが、ポート番号やSYN/ACK番号を使って多重化している。

HTTPはTCP/IPの上でコマレスを行う。標準だとKeepAlive無効(HTTP 1.0)だったり、html, css, jsなどの複数ファイルを要求するために1つずつ要求しないといけなかった。

HTTP2はTCPのセッションを張って、いろいろ工夫しているみたい(未調査)

gRPCはHTTP2で通信しつつ、オブジェクトのシリアライズと復元までやってくれる便利なやつ。あまり詳しくない。RPCです。

WebSocketはTCPのセッションを使って双方向通信を行う。これも未調査。通常のTCP Socketとは異なり、recvでヘッダサイズを確認する必要がなく、受信側のコールバックが呼ばれる仕組みが備わっているとのこと。