UNIXドメインソケット

ソケット (BSD) > UNIXドメインソケット UNIXドメインソケット（英: UNIX domain socket）は単一マシン上の高効率なプロセス間通信に用いられる機能・インタフェースの一種である。 プロセス間通信 (Inter-Process Communication: IPC) は一般的に名前付きパイプやBSDソケットを利用したTCP通信などで実現できる。UNIXドメインソケットはBSDソケットの一種であり、単一マシン上でのプロセス間通信を目的としている。ソケット通信がもつ双方性・プロセスfork不要といった特徴を備えつつ、単一マシン上の通信である（=インターネットを介さない）ことを生かした高効率な通信を可能にしている。 UNIXドメインソケットは、アドレス・名前空間としてファイルシステムを使用している。これらは、ファイルシステム内のinodeとしてプロセスから参照される。これは、2つのプロセスが通信するために、同じソケットを開くことができる。しかし、コミュニケーションは、完全にオペレーティングシステムのカーネル内で発生する。データを送ることに加えて、プロセスは、sendmsg()およびrecvmsg()システムコールを使用してUNIXドメインソケット接続を経由してファイル記述子を送信することができる。 POSIXはBSDソケットとしてUNIXドメインソケットインタフェースを提供している。BSDソケットのAF_UNIXprotocol familyがUNIXドメインソケットに相当する。受け入れ可能なsocket typeはSOCK_STREAM・SOCK_DGRAM・SOCK_SEQPACKETの3種類である。 Go言語（の実行環境）では、Microsoft Windows上でもUNIXドメインソケットを使ったプログラムを動かすことができる。 特殊なUNIXドメインソケットとして、socketpair()を用いて作られるものがある。socketpair()はUNIXドメインソケットを2点作成し、一方のソケットに書き込んだデータがもう一方のソケットから読み出せる。パイプが一方通行の読み書きしかできないのに対し、socketpair()にて作成したソケットでは双方向通信が可能である。 socket() 関数における第1引数の domain に AF_UNIX を指定する。第2引数の type は SOCK_STREAM, SOCK_DGRAM どちらも利用可能。そして、クライアント側の場合、その後 connect() を呼ぶ。 以下のライブラリなどで利用可能。 junixsocket JUDS また、JEP380によりJava 16よりjava.nio.channels.SocketChannelで利用可能 Android SDKに、Java/Kotlinから利用できる android.net.LocalSocket クラスが実装されている。 Mono.Unix.UnixClient クラスで利用可能。 URL を unix:// もしくは udg:// で始めることで利用可能。 C99 でのサーバ側のソースコード例は以下の通り。 #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> int main(void) { // サーバーソケット作成 int sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { perror("socket"); return 1; } // struct sockaddr_un 作成 struct sockaddr_un sa = {0}; sa.sun_family = AF_UNIX; strcpy(sa.sun_path, "/tmp/unix-domain-socket"); // 既に同一ファイルが存在していたら削除 remove(sa.sun_path); // バインド if (bind(sock, (struct sockaddr*) &sa, sizeof(struct sockaddr_un)) == -1) { perror("bind"); goto bail; } // リッスン if (listen(sock, 128) == -1) { perror("listen"); goto bail; } while (1) { // クライアントの接続を待つ int fd = accept(sock, NULL, NULL); if (fd == -1) { perror("accept"); goto bail; } // 受信 char buffer[4096]; int recv_size = read(fd, buffer, sizeof(buffer) - 1); if (recv_size == -1) { perror("read"); close(fd); goto bail; } // 受信内容を表示 buffer[recv_size] = '\0'; printf("message: %s\n", buffer); // ソケットのクローズ if (close(fd) == -1) { perror("close"); goto bail; } } bail: // エラーが発生した場合の処理 close(sock); return 1; } C99 でのクライアント側のソースコード例は以下の通り。 #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #define MESSAGE "Hello World!" int main(void) { // ソケット作成 int sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { perror("socket"); return 1; } // struct sockaddr_un 作成 struct sockaddr_un sa = {0}; sa.sun_family = AF_UNIX; strcpy(sa.sun_path, "/tmp/unix-domain-socket"); // 接続 if (connect(sock, (struct sockaddr*) &sa, sizeof(struct sockaddr_un)) == -1) { perror("connect"); goto bail; } // 送信 if (write(sock, MESSAGE, strlen(MESSAGE)) == -1) { perror("write"); goto bail; } // クローズ close(sock); return 0; bail: // エラーが発生した場合の処理 close(sock); return 1; } LinuxカーネルにBSDソケット AF_UNIXprotocol familyとして実装されている。SOCKET(2)をはじめとするシステムコールをAPIとして公開している。 LinuxにおけるUNIXドメインソケットは、バッファ（メモリ）への書き込みと読み込みという非常にシンプルな仕組みで実装されている。 Linuxカーネルのlinux/net/unix/af_unix.c (GitHub mirror) に実装される。SOCK_STREAMの場合、カーネル内部ではソケットバッファへのメッセージコピー・peerがもつsk_receive_queueのtail、受信側のsk_data_ready呼び出し、を繰り返すことでデータを転送する。 UNIXドメインソケットは単一マシン上のIPCが前提である。ゆえにTCPのようなプロトコルスイートは不要であり、プロトコルの重層が生むデータの入れ子構造を持たない。またネットワークに由来するパケットロスや到達順序保証の対応も必要ないため、バッファのread/writeというシンプルな仕組みで実装されている。結果として高効率なIPCが可能となっている。