Linux nettoie-t-il automatiquement les sockets de domaine abstrait?

15

Il y a une excellente réponse sur StackOverflow pour fournir un meilleur verrouillage pour les démons (synthétisé à partir d' Eduardo Fleury ) qui ne dépend pas du mécanisme de verrouillage de fichier PID commun pour les démons. Il y a beaucoup de bons commentaires sur les raisons pour lesquelles les fichiers de verrouillage PID peuvent parfois causer des problèmes, donc je ne les retravaillerai pas ici.

En bref, la solution s'appuie sur des sockets de domaine d'espace de noms abstraits Linux, qui gardent une trace des sockets par nom, plutôt que de s'appuyer sur des fichiers, qui peuvent rester après que le démon soit SIGKILL. L'exemple montre que Linux semble libérer le socket une fois le processus mort.

Mais je ne trouve pas de documentation définitive sous Linux qui indique exactement ce que fait Linux avec la socket abstraite lorsque le processus lié est SIGKILL. Est-ce que quelqu'un sait?

Autrement dit, quand précisément la prise abstraite est-elle libérée pour être réutilisée?

Je ne veux pas remplacer le mécanisme de fichier PID par des sockets abstraites à moins qu'il ne résout définitivement le problème.

CivFan
la source
3
Je ne trouve rien qui réponde directement à cela. Mais comme il n'y a pas d'API pour supprimer les sockets abstraites, il semble qu'elles devraient être gérées automatiquement par le noyau. Lorsqu'il n'y a aucun processus avec le socket ouvert, il devrait disparaître.
Barmar
@Barmar Assez juste. Voulez-vous l'ajouter comme réponse?
CivFan
Je préférerais avoir des informations plus précises.
Barmar

Réponses:

5

Oui, Linux "nettoie" automatiquement les sockets abstraites dans la mesure où le nettoyage est même logique. Voici un exemple de travail minimal avec lequel vous pouvez vérifier ceci:

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

Exécutez ce programme en tant que ./a.out /test-socket &, puis exécutez ss -ax | grep test-socket, et vous verrez le socket utilisé. Ensuite kill %./a.out, et ss -axmontrera que le socket a disparu.

Cependant, la raison pour laquelle vous ne pouvez trouver ce nettoyage dans aucune documentation est qu'il ne nettoie pas vraiment dans le même sens que les sockets de domaine Unix non abstraits doivent être nettoyés. Un socket non abstrait alloue en fait un inode et crée une entrée dans un répertoire, qui doit être nettoyée dans le système de fichiers sous-jacent. En revanche, pensez à une socket abstraite plus comme un numéro de port TCP ou UDP. Bien sûr, si vous liez un port TCP puis quittez, ce port TCP sera à nouveau libre. Mais quel que soit le nombre 16 bits que vous avez utilisé, il existe toujours abstraitement et a toujours existé. L'espace de noms des numéros de port est 1-65535 et ne change jamais ou n'a pas besoin d'être nettoyé.

Donc, pensez simplement au nom de socket abstrait comme un numéro de port TCP ou UDP, simplement choisi parmi un ensemble beaucoup plus grand de numéros de port possibles qui ressemblent à des chemins d'accès mais qui ne le sont pas. Vous ne pouvez pas lier deux fois le même numéro de port (sauf SO_REUSEADDRou SO_REUSEPORT). Mais la fermeture du socket (explicitement ou implicitement en terminant) libère le port, sans rien à nettoyer.

user3188445
la source
Il a déjà passé le test du canard. C'est bien pour certaines choses, comme python, mais j'attends plus pour les fonctionnalités du noyau Linux. Prenez vos exemples de ports TCP / UDP - il y a beaucoup de documentation décrivant précisément quand le port peut être réutilisé.
CivFan
2
Et pourquoi cette documentation ne s'applique-t-elle pas également aux sockets abstraites? Avez-vous une référence? Une meilleure question pourrait être de savoir où la complication de nettoyage supplémentaire pour les sockets de domaine Unix non abstraits est documentée. Sur mon système, c'est dans unix (7) , qui dit "Linux prend également en charge un espace de noms abstrait qui est indépendant du système de fichiers." Donc, pour moi, "indépendant du système de fichiers" n'implique aucun nettoyage spécifique au système de fichiers.
user3188445
5

J'ai posté cette question il y a plus d'un an et je n'ai jamais été très satisfait du manque de documentation définitive. Je pensais que je vérifierais à nouveau la documentation Linux pour toute mise à jour, et j'étais heureux de voir ceci :

Prises abstraites

Les autorisations de socket n'ont aucune signification pour les sockets abstraites: le processus umask (2) n'a aucun effet lors de la liaison d'une socket abstraite, et la modification de la propriété et des autorisations de l'objet (via fchown (2) et fchmod (2)) n'a aucun effet sur le accessibilité de la prise.

Les sockets abstraites disparaissent automatiquement lorsque toutes les références ouvertes au socket sont fermées.

De plus, l' interface de programmation Linux de Michael Kerrisk couvre la question (publiée en croix à partir de cette autre réponse ):

57.6 L'espace de noms de socket abstrait Linux

Le soi-disant espace de noms abstrait est une fonctionnalité spécifique à Linux qui nous permet de lier une socket de domaine UNIX à un nom sans que ce nom soit créé dans le système de fichiers. Cela présente quelques avantages potentiels:

  • Nous n'avons pas à nous soucier des collisions possibles avec des noms existants dans le système de fichiers.
  • Il n'est pas nécessaire de dissocier le chemin d'accès du socket lorsque nous avons fini d'utiliser le socket. Le nom abstrait est automatiquement supprimé lorsque le socket est fermé.
  • Nous n'avons pas besoin de créer un chemin d'accès au système de fichiers pour le socket. Cela peut être utile dans un environnement chroot ou si nous n'avons pas accès en écriture à un système de fichiers.

Pour créer une liaison abstraite, nous spécifions le premier octet du champ sun_path comme un octet nul (\ 0). [...]

Je pense que, avec la réponse de @ user3188445, cela clarifie la question très précisément.

Cela dit, il y a toujours une hypothèse faite ici, que les processus qui sont SIGKILL auraient tous les sockets ouverts fermés. Cela semble une hypothèse raisonnable, mais je n'ai pas de documentation définissant ce comportement.

CivFan
la source
1
Concernant le dernier paragraphe: les sockets sont des descripteurs de fichiers et tous les descripteurs de fichiers ouverts sont fermés à la fin d'un processus. Ish. Plus spécifiquement, un socket est un fichier ouvert, des fichiers ouverts peuvent être transmis, par exemple hérités par des processus enfants. Vous devez donc vous assurer d'appeler socket avec SOCK_CLOEXECau cas où un code (y compris une bibliothèque) ferait jamais fork () + exec (). La création de processus enfants supplémentaires en utilisant fork () sans exec () est moins courante; vous savez probablement déjà si vous faites cela.
sourcejedi
Il n'est pas nécessaire de dissocier ... - euh, puisqu'il n'y a pas de chemin, il n'est pas possible de dissocier, pas simplement inutile.
domen