Bereinigt Linux automatisch abstrakte Domain-Sockets?

15

Auf StackOverflow gibt es eine großartige Antwort darauf , eine bessere Sperre für Daemons (synthetisiert von Eduardo Fleury ) bereitzustellen , die nicht vom allgemeinen PID-Dateisperrmechanismus für Daemons abhängt. Es gibt dort viele gute Kommentare darüber, warum PID-Sperrdateien manchmal Probleme verursachen können, deshalb werde ich sie hier nicht erneut aufbereiten.

Kurz gesagt, die Lösung stützt sich auf abstrakte Linux-Namespace-Domain-Sockets, die die Sockets nach Namen protokollieren, anstatt sich auf Dateien zu verlassen, die nach dem SIGKILL-Vorgang des Daemons bestehen bleiben können. Das Beispiel zeigt, dass Linux den Socket freizugeben scheint, sobald der Prozess beendet ist.

Aber ich kann in Linux keine definitive Dokumentation finden, die besagt, was genau Linux mit dem abstrakten Socket macht, wenn der gebundene Prozess SIGKILL'd ist. Weiß jemand?

Anders ausgedrückt, wann genau kann der abstrakte Socket wieder verwendet werden?

Ich möchte den PID-Dateimechanismus nicht durch abstrakte Sockets ersetzen, es sei denn, dies löst das Problem endgültig.

CivFan
quelle
3
Ich kann nichts finden, was dies direkt beantwortet. Da es jedoch keine API zum Entfernen abstrakter Sockets gibt, müssten sie anscheinend automatisch vom Kernel verwaltet werden. Wenn bei geöffnetem Socket keine Prozesse vorhanden sind, sollte der Socket verschwinden.
Barmar
@Barmar Fair genug. Möchtest du es als Antwort hinzufügen?
CivFan,
Ich hätte lieber genauere Informationen.
Barmar

Antworten:

5

Ja, Linux "räumt" abstrakte Sockets automatisch auf, sofern das Aufräumen überhaupt Sinn macht. Hier ist ein minimales Arbeitsbeispiel, anhand dessen Sie dies überprüfen können:

#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();
}

Führen Sie dieses Programm als ./a.out /test-socket &aus ss -ax | grep test-socket, und führen Sie dann aus , und Sie werden den verwendeten Socket sehen. Dann kill %./a.outwird und ss -axzeigen, dass die Steckdose weg ist.

Der Grund, warum Sie diese Bereinigung in keiner Dokumentation finden können, ist, dass sie nicht wirklich in dem Sinne bereinigt werden kann, in dem nicht abstrakte Sockets für Unix-Domänen bereinigt werden müssen. Ein nicht-abstrakter Socket weist tatsächlich einen Inode zu und erstellt einen Eintrag in einem Verzeichnis, der im zugrunde liegenden Dateisystem bereinigt werden muss. Stellen Sie sich einen abstrakten Socket dagegen eher wie eine TCP- oder UDP-Portnummer vor. Sicher, wenn Sie einen TCP-Port binden und dann beenden, ist dieser TCP-Port wieder frei. Aber welche 16-Bit-Zahl Sie auch immer verwendet haben, sie existiert immer noch abstrakt und hat es immer getan. Der Namespace der Portnummern ist 1-65535 und muss niemals geändert oder gereinigt werden.

Stellen Sie sich also den abstrakten Socket-Namen wie eine TCP- oder UDP-Portnummer vor, die Sie aus einem viel größeren Satz möglicher Portnummern ausgewählt haben, die zwar wie Pfadnamen aussehen, dies aber nicht sind. Sie können dieselbe Portnummer nicht zweimal binden (außer SO_REUSEADDRoder SO_REUSEPORT). Durch Schließen des Sockets (explizit oder implizit durch Beenden) wird der Port jedoch freigegeben, und es muss nichts mehr aufgeräumt werden.

user3188445
quelle
Es hat den Ententest bereits bestanden. Für einige Dinge, wie Python, ist das in Ordnung, aber ich erwarte mehr für Linux-Kernelfunktionen. Nehmen Sie ein Beispiel für TCP / UDP-Ports - es gibt zahlreiche Dokumentationen, die genau beschreiben, wann der Port wiederverwendet werden kann.
CivFan
2
Und warum gilt diese Dokumentation nicht auch für abstrakte Sockets? Haben Sie eine Referenz? Eine bessere Frage könnte sein, wo die zusätzliche Bereinigungskomplikation für nicht abstrakte Unix-Domain-Sockets dokumentiert ist. Auf meinem System ist das in Unix (7) , wo steht, "Linux unterstützt auch einen abstrakten Namespace, der unabhängig vom Dateisystem ist." Für mich bedeutet "unabhängig vom Dateisystem" keine dateisystemspezifische Bereinigung.
user3188445
5

Ich habe diese Frage vor über einem Jahr gestellt und war mit dem Mangel an endgültiger Dokumentation nie ganz zufrieden. Ich dachte, ich würde die Linux-Dokumentation erneut auf Aktualisierungen überprüfen und freute mich über Folgendes :

Abstrakte Sockel

Socket-Berechtigungen haben keine Bedeutung für abstrakte Sockets: Der Prozess umask (2) hat keine Auswirkung auf das Binden eines abstrakten Sockets, und das Ändern der Eigentumsrechte und Berechtigungen des Objekts (über fchown (2) und fchmod (2)) hat keine Auswirkung auf die Zugänglichkeit der Steckdose.

Abstrakte Sockets verschwinden automatisch, wenn alle offenen Verweise auf den Socket geschlossen werden.

Auch die Linux - Programmierschnittstelle von Michael Kerrisk deckt die Frage (von Cross-gepostet dieser anderen Antwort ):

57.6 Der Linux Abstract Socket Namespace

Der sogenannte abstrakte Namespace ist eine Linux-spezifische Funktion, mit der wir einen UNIX-Domain-Socket an einen Namen binden können, ohne dass dieser Name im Dateisystem erstellt wird. Dies bietet einige potenzielle Vorteile:

  • Wir müssen uns keine Gedanken über mögliche Kollisionen mit vorhandenen Namen im Dateisystem machen.
  • Es ist nicht erforderlich, den Socket-Pfadnamen zu trennen, wenn der Socket nicht mehr verwendet wird. Der abstrakte Name wird automatisch entfernt, wenn der Socket geschlossen wird.
  • Wir müssen keinen Dateisystempfadnamen für den Socket erstellen. Dies kann in einer Chroot-Umgebung nützlich sein oder wenn wir keinen Schreibzugriff auf ein Dateisystem haben.

Um eine abstrakte Bindung zu erstellen, geben wir das erste Byte des sun_path- Felds als Nullbyte (\ 0) an. [...]

Ich denke, dass dies zusammen mit der Antwort von @ user3188445 die Frage sehr genau klärt.

Trotzdem wird hier immer noch davon ausgegangen, dass Prozesse, die SIGKILL-fähig sind, alle offenen Sockets geschlossen haben. Das scheint eine vernünftige Annahme zu sein, aber ich habe keine Dokumentation, die dieses Verhalten definiert.

CivFan
quelle
1
Letzter Absatz: Sockets sind Dateideskriptoren, und alle geöffneten Dateideskriptoren werden geschlossen, wenn ein Prozess beendet wird. Ish. Insbesondere ist ein Socket eine offene Datei, offene Dateien können weitergegeben werden, z. B. von untergeordneten Prozessen geerbt. Sie sollten also sicherstellen, dass Sie socket mit aufrufen, SOCK_CLOEXECfalls Code (einschließlich einer Bibliothek) jemals fork () + exec () ausführt. Das Erstellen zusätzlicher untergeordneter Prozesse mit fork () ohne exec () ist seltener. Sie wissen wahrscheinlich schon, ob Sie das tun.
Sourcejedi
Es ist nicht notwendig, die Verknüpfung zu lösen ... - ähm, da es keinen Pfadnamen gibt, ist es nicht möglich, die Verknüpfung zu lösen, nicht nur unnötig.
Domen