Linux ripulisce automaticamente i socket di dominio astratti?


15

C'è una grande risposta su StackOverflow sulla fornitura di un blocco migliore per i demoni (sintetizzato da Eduardo Fleury ) che non dipende dal meccanismo di blocco dei file PID comune per i demoni. Ci sono molti buoni commenti lì sul perché i file di blocco PID a volte possono causare problemi, quindi non li riproverò qui.

In breve, la soluzione si basa su socket di dominio dello spazio dei nomi astratti di Linux, che tengono traccia dei socket in base al nome, anziché fare affidamento sui file, che possono rimanere in gioco dopo che il demone è SIGKILL'd. L'esempio mostra che Linux sembra liberare il socket una volta che il processo è morto.

Ma non riesco a trovare una documentazione definitiva in Linux che dice cosa fa esattamente Linux con il socket astratto quando il processo associato è SIGKILL. Qualcuno sa?

Detto in altro modo, quando precisamente la presa astratta viene liberata per essere riutilizzata?

Non voglio sostituire il meccanismo del file PID con socket astratti a meno che non risolva definitivamente il problema.


3
Non riesco a trovare nulla che risponda direttamente a questo. Ma poiché non esiste alcuna API per la rimozione di socket astratti, sembra che dovrebbero essere gestiti automaticamente dal kernel. Quando non ci sono processi con il socket aperto, dovrebbe andare via.
Barmar,

@Barmar Abbastanza giusto. Vuoi aggiungerlo come risposta?
CivFan,

Preferirei avere informazioni più precise.
Barmar,

Risposte:


5

Sì, Linux "ripulisce automaticamente" le prese astratte nella misura in cui anche la pulizia ha senso. Ecco un esempio minimo di lavoro con il quale è possibile verificarlo:

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

Esegui questo programma come ./a.out /test-socket &, quindi esegui ss -ax | grep test-sockete vedrai il socket in uso. Quindi kill %./a.out, e ss -axmostrerà che il socket non c'è più.

Tuttavia, il motivo per cui non è possibile trovare questo clean-up in alcuna documentazione è che non si sta realmente ripulendo nello stesso senso in cui i socket unix di dominio non astratti devono essere ripuliti. Un socket non astratto alloca effettivamente un inode e crea una voce in una directory, che deve essere ripulita nel file system sottostante. Al contrario, pensa a un socket astratto più simile a un numero di porta TCP o UDP. Certo, se associ una porta TCP e poi esci, quella porta TCP sarà di nuovo libera. Ma qualsiasi numero a 16 bit che hai usato esiste ancora in modo astratto e lo ha sempre fatto. Lo spazio dei nomi dei numeri di porta è 1-65535 e non cambia mai o deve essere pulito.

Quindi basta pensare al nome del socket astratto come un numero di porta TCP o UDP, appena scelto da un insieme molto più ampio di possibili numeri di porta che sembrano avere nomi di percorsi ma non lo sono. Non è possibile associare due volte lo stesso numero di porta (blocco SO_REUSEADDRo SO_REUSEPORT). Ma chiudere il socket (esplicitamente o implicitamente terminando) libera la porta, senza lasciare nulla da ripulire.


Ha già superato il test dell'anatra. Va bene per alcune cose, come Python, ma mi aspetto di più per le funzionalità del kernel Linux. Prendi i tuoi esempi di porte TCP / UDP: c'è molta documentazione che descrive esattamente quando la porta può essere riutilizzata.
CivFan,

2
E perché quella documentazione non si applica allo stesso modo ai socket astratti? Hai un riferimento? Una domanda migliore potrebbe essere dove è documentata la complicazione di pulizia extra per socket di dominio Unix non astratti. Sul mio sistema, questo è in unix (7) , che dice "Linux supporta anche uno spazio dei nomi astratto che è indipendente dal filesystem". Quindi per me "indipendente dal filesystem" non implica alcuna pulizia specifica del file system.
user3188445

5

Ho pubblicato questa domanda più di un anno fa e non sono mai stato abbastanza soddisfatto della mancanza di documentazione definitiva. Ho pensato di controllare nuovamente la documentazione Linux per eventuali aggiornamenti, ed ero felice di vedere questo :

Prese astratte

Le autorizzazioni socket non hanno alcun significato per i socket astratti: il processo umask (2) non ha alcun effetto quando si associa un socket astratto e la modifica della proprietà e delle autorizzazioni dell'oggetto (tramite fchown (2) e fchmod (2)) non ha alcun effetto sul accessibilità della presa.

Le prese astratte scompaiono automaticamente quando tutti i riferimenti aperti alla presa vengono chiusi.

Inoltre, l'interfaccia di programmazione Linux di Michael Kerrisk copre la domanda (postata in modo incrociato da questa altra risposta ):

57.6 The Linux Abstract Socket Namespace

Il cosiddetto spazio dei nomi astratto è una funzionalità specifica di Linux che ci consente di associare un socket di dominio UNIX a un nome senza che tale nome venga creato nel file system. Ciò offre alcuni potenziali vantaggi:

  • Non dobbiamo preoccuparci di possibili collisioni con nomi esistenti nel file system.
  • Non è necessario scollegare il percorso del socket dopo aver finito di utilizzare il socket. Il nome astratto viene rimosso automaticamente alla chiusura del socket.
  • Non è necessario creare un percorso del file system per il socket. Ciò può essere utile in un ambiente chroot o se non abbiamo accesso in scrittura a un file system.

Per creare un'associazione astratta, specifichiamo il primo byte del campo sun_path come byte null (\ 0). [...]

Suppongo che, insieme alla risposta di @ user3188445, questo chiarisca la domanda in modo molto preciso.

Detto questo, c'è ancora un'ipotesi fatta qui, che i processi che sono SIGKILL avrebbero chiuso tutti i socket aperti. Sembra un presupposto ragionevole, ma non ho documentazione che definisce tale comportamento.


1
Per quanto riguarda l'ultimo paragrafo: i socket sono descrittori di file e tutti i descrittori di file aperti vengono chiusi all'uscita da un processo. Ish. Più specificamente, un socket è un file aperto, i file aperti possono essere passati, ad esempio, ereditati da processi figlio. Quindi dovresti assicurarti di chiamare socket con SOCK_CLOEXECqualsiasi codice (inclusa una libreria) che faccia fork () + exec (). La creazione di processi secondari aggiuntivi utilizzando fork () senza exec () è meno comune; probabilmente sai già se lo stai facendo.
sourcejedi

Non è necessario scollegare ... - um, poiché non esiste un nome percorso, non è possibile scollegare, non solo inutile.
domen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.