Linuxは抽象的なドメインソケットを自動的にクリーンアップしますか?


15

StackOverflowには、デーモンの一般的なPIDファイルロックメカニズムに依存しない(Eduardo Fleuryから合成された)デーモンに対してより良いロックを提供するという素晴らしい回答があります。PIDロックファイルが問題を引き起こすことがある理由については、多くの良いコメントがありますので、ここではそれらを再ハッシュしません。

要するに、このソリューションはLinuxの抽象名前空間ドメインソケットに依存しています。これは、デーモンがSIGKILLされた後もファイルに依存するのではなく、名前でソケットを追跡します。この例は、プロセスが停止するとLinuxがソケットを解放するように見えることを示しています。

しかし、バインドされたプロセスがSIGKILLされたとき、Linuxが抽象ソケットで正確に何をするかを示す明確なドキュメントをLinuxで見つけることができません。誰か知っている?

別の言い方をすると、抽象ソケットが正確に解放されて再び使用できるようになるのはいつですか?

問題を確実に解決しない限り、PIDファイルメカニズムを抽象ソケットに置き換えたくありません。


3
これに直接答えるものが見つかりません。しかし、抽象ソケットを削除するためのAPIがないため、抽象ソケットはカーネルによって自動的に管理される必要があるようです。ソケットが開いているプロセスがなくなると、プロセスはなくなります。
バーマー

@Barmar Fair十分。それを答えとして追加しますか?
CivFan

もっと明確な情報が欲しいです。
バーマー

回答:


5

はい、Linuxは自動的に抽象ソケットを「クリーンアップ」し、クリーンアップしても意味があります。これを確認できる最小限の作業例を次に示します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>

int
main(int argc, char **argv)
{
  int s;
  struct sockaddr_un sun;

  if (argc != 2 || strlen(argv[1]) + 1 > sizeof(sun.sun_path)) {
    fprintf(stderr, "usage: %s abstract-path\n", argv[0]);
    exit(1);
  }

  s = socket(AF_UNIX, SOCK_STREAM, 0);
  if (s < 0) {
    perror("socket");
    exit(1);
  }
  memset(&sun, 0, sizeof(sun));
  sun.sun_family = AF_UNIX;
  strcpy(sun.sun_path + 1, argv[1]);
  if (bind(s, (struct sockaddr *) &sun, sizeof(sun))) {
    perror("bind");
    exit(1);
  }
  pause();
}

このプログラムをとして実行し./a.out /test-socket &、次にを実行するss -ax | grep test-socketと、使用中のソケットが表示されます。次にkill %./a.outss -axソケットがなくなったことを示します。

ただし、ドキュメントでこのクリーンアップが見つからない理由は、非抽象unixドメインソケットのクリーンアップが必要なのと同じ意味で実際にはクリーンアップされていないためです。非抽象ソケットは実際にiノードを割り当て、ディレクトリにエントリを作成します。このエントリは、基礎となるファイルシステムでクリーンアップする必要があります。対照的に、TCPまたはUDPポート番号のような抽象ソケットを考えてください。もちろん、TCPポートをバインドして終了すると、そのTCPポートは再び解放されます。ただし、使用した16ビットの数値は抽象的に存在し、常に存在していました。ポート番号の名前空間は1〜65535であり、変更したり、クリーニングする必要はありません。

したがって、TCPまたはUDPポート番号のような抽象的なソケット名を考えてみてください。パス名のように見えるがそうではない可能性のあるはるかに大きなポート番号のセットから選択されただけです。同じポート番号を2回バインドすることはできません(禁止SO_REUSEADDRまたはSO_REUSEPORT)。ただし、ソケットを閉じると(明示的または暗黙的に終了することで)ポートが解放され、クリーンアップするものは何も残りません。


すでにアヒルのテストに合格しています。pythonなど、いくつかのものにはそれで問題ありませんが、Linuxカーネル機能にはもっと期待しています。TCP / UDPポートの例を見てください。ポートをいつ再利用できるかを正確に説明したドキュメントがたくさんあります。
CivFan

2
そして、なぜその文書は抽象ソケットにも等しく適用されないのですか?参考文献はありますか?より良い質問は、非抽象Unixドメインソケットの余分なクリーンアップの複雑さをどこで文書化するかです。私のシステムでは、それはunix(7)にあり、「Linuxはファイルシステムに依存しない抽象的な名前空間もサポートしています。」したがって、私にとって「ファイルシステムに依存しない」とは、ファイルシステム固有のクリーンアップがないことを意味します。
user3188445

5

私は1年以上前にこの質問を投稿しましたが、決定的なドキュメントがないことに満足することはありませんでした。更新についてLinuxのドキュメントをもう一度確認すると思ったので、これを見て喜んでい

抽象ソケット

ソケットパーミッションは抽象ソケットに対して意味がありません:プロセスumask(2)は抽象ソケットをバインドするときに効果がなく、オブジェクトの所有権とパーミッションを(fchown(2)およびfchmod(2)を介して)変更しても、ソケットのアクセシビリティ。

ソケットへの開いている参照がすべて閉じられると、抽象ソケットは自動的に消えます。

また、Michael KerriskによるLinux Programming Interfaceが質問をカバーしています(他の回答からクロスポストされています)。

57.6 Linuxの抽象ソケット名前空間

いわゆる抽象ネームスペースはLinux固有の機能であり、ファイルシステムで作成された名前なしにUNIXドメインソケットを名前にバインドできます。これには、いくつかの潜在的な利点があります。

  • ファイルシステム内の既存の名前と衝突する可能性について心配する必要はありません。
  • ソケットの使用が終了したら、ソケットのパス名をリンク解除する必要はありません。ソケットが閉じられると、抽象名は自動的に削除されます。
  • ソケットのファイルシステムパス名を作成する必要はありません。これは、chroot環境、またはファイルシステムへの書き込みアクセス権がない場合に便利です。

抽象バインディングを作成するには、sun_pathフィールドの最初のバイトをヌルバイト(\ 0)として指定します 。[...]

@ user3188445の答えと一緒に、これにより質問が非常に正確にクリアされると思います。

ただし、SIGKILLされたプロセスでは、開いているすべてのソケットが閉じられるという前提があります。それは合理的な仮定のように思えますが、その動作を定義するドキュメントはありません。


1
最後の段落について:ソケットはファイル記述子であり、プロセスが終了すると、開いているファイル記述子はすべて閉じられます。Hは。より具体的には、ソケットは開かれたファイルであり、開かれたファイルは、子プロセスに継承されるなど、受け渡すことができます。そのためSOCK_CLOEXEC、コード(ライブラリを含む)がfork()+ exec()を実行する場合に備えて、必ずsocketを呼び出してください。exec()なしでfork()を使用して追加の子プロセスを作成することはあまり一般的ではありません。あなたはおそらくあなたがそれをやっているかどうかすでに知っています。
sourcejedi

リンクを解除する必要はありません...-えーと、パス名がないので、リンクを解除することはできません。不要なだけではありません。
堂免
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.