Czy Linux automatycznie czyści abstrakcyjne gniazda domen?


15

Na StackOverflow znajduje się świetna odpowiedź na temat zapewnienia lepszej blokady demonów (zsyntetyzowanej przez Eduardo Fleury ), która nie zależy od wspólnego mechanizmu blokowania plików PID dla demonów. Istnieje wiele dobrych komentarzy na temat tego, dlaczego pliki blokujące PID mogą czasami powodować problemy, więc nie będę ich tutaj ponownie przeglądać.

Krótko mówiąc, rozwiązanie opiera się na gniazdach domeny abstrakcyjnej przestrzeni nazw systemu Linux, które śledzą gniazda według nazwy, a nie na plikach, które mogą pozostać po SIGKILL'ie demona. Przykład pokazuje, że Linux wydaje się zwalniać gniazdo po zakończeniu procesu.

Ale nie mogę znaleźć ostatecznej dokumentacji w systemie Linux, która mówi, co dokładnie Linux robi z gniazdem abstrakcyjnym, gdy związany proces jest SIGKILL. Czy ktoś wie?

Innymi słowy, kiedy dokładnie abstrakcyjne gniazdo może zostać ponownie użyte?

Nie chcę zastępować mechanizmu pliku PID abstrakcyjnymi gniazdami, chyba że ostatecznie rozwiąże problem.


3
Nie mogę znaleźć niczego, co bezpośrednio na to odpowie. Ale ponieważ nie ma API do usuwania abstrakcyjnych gniazd, wygląda na to, że jądro musiałoby nimi zarządzać automatycznie. Gdy nie ma żadnych procesów z otwartym gniazdem, powinno ono zniknąć.
Barmar,

@Barmar Do przyjęcia. Chcesz dodać to jako odpowiedź?
CivFan,

Wolałbym mieć bardziej konkretne informacje.
Barmar

Odpowiedzi:


5

Tak, linux automatycznie „czyści” abstrakcyjne gniazda do tego stopnia, że ​​nawet czyszczenie ma sens. Oto minimalny działający przykład, za pomocą którego możesz to zweryfikować:

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

Uruchom ten program jako ./a.out /test-socket &, a następnie uruchom ss -ax | grep test-socket, a zobaczysz używane gniazdo. Wtedy kill %./a.out, i ss -axpokaże gniazdko nie ma.

Jednak powodem, dla którego nie można znaleźć takiego czyszczenia w żadnej dokumentacji, jest to, że tak naprawdę nie jest to czyszczenie w tym samym sensie, co nie abstrakcyjne gniazda w domenie unixowej wymagają czyszczenia. Nie-abstrakcyjne gniazdo faktycznie przydziela i-węzeł i tworzy wpis w katalogu, który należy wyczyścić w podstawowym systemie plików. Pomyśl o abstrakcyjnym gnieździe bardziej przypominającym numer portu TCP lub UDP. Jasne, jeśli powiążesz port TCP, a następnie wyjdziesz, ten port TCP znów będzie wolny. Ale jakikolwiek numer 16-bitowy, którego użyłeś, nadal istnieje abstrakcyjnie i zawsze istniał. Przestrzeń nazw numerów portów to 1-65535 i nigdy się nie zmienia ani nie wymaga czyszczenia.

Pomyśl więc o abstrakcyjnej nazwie gniazda, takiej jak numer portu TCP lub UDP, wybrany z dużo większego zestawu możliwych numerów portów, które wyglądają jak nazwy ścieżek, ale nimi nie są. Nie można powiązać tego samego numeru portu dwa razy (blokowanie SO_REUSEADDRlub SO_REUSEPORT). Ale zamknięcie gniazda (jawnie lub niejawnie przez zakończenie) uwalnia port, nie pozostawiając nic do wyczyszczenia.


Przeszedł już test kaczki. To jest w porządku dla niektórych rzeczy, takich jak Python, ale oczekuję więcej dla funkcji jądra Linuksa. Weź przykłady portów TCP / UDP - jest mnóstwo dokumentacji opisującej dokładnie, kiedy port może być ponownie użyty.
CivFan,

2
I dlaczego ta dokumentacja nie dotyczy w równym stopniu gniazd abstrakcyjnych? Czy masz referencje? Lepszym pytaniem może być to, gdzie udokumentowano dodatkową komplikację czyszczenia dla nieabstrakcyjnych gniazd domeny Unix. W moim systemie jest to wersja unix (7) , która mówi: „Linux obsługuje także abstrakcyjną przestrzeń nazw niezależną od systemu plików”. Tak więc dla mnie „niezależny od systemu plików” nie wymaga czyszczenia specyficznego dla systemu plików.
user3188445,

5

Zadałem to pytanie ponad rok temu i nigdy nie byłem zadowolony z braku ostatecznej dokumentacji. Pomyślałem, że ponownie sprawdzę dokumentację Linuksa pod kątem aktualizacji i cieszę się, widząc to :

Gniazda abstrakcyjne

Uprawnienia do gniazd nie mają znaczenia dla gniazd abstrakcyjnych: umask procesu (2) nie ma wpływu podczas wiązania gniazda abstrakcyjnego, a zmiana własności i uprawnień do obiektu (za pomocą fchown (2) i fchmod (2)) nie ma wpływu na dostępność gniazda.

Gniazda abstrakcyjne automatycznie znikają po zamknięciu wszystkich otwartych odniesień do gniazda.

Również interfejs programowania Linuksa autorstwa Michaela Kerrisk obejmuje pytanie (zamieszczone w innej odpowiedzi ):

57.6 Przestrzeń nazw Linux Abstract Socket

Tak zwana abstrakcyjna przestrzeń nazw to funkcja specyficzna dla systemu Linux, która pozwala nam powiązać gniazdo domeny UNIX z nazwą bez tworzenia tej nazwy w systemie plików. Zapewnia to kilka potencjalnych korzyści:

  • Nie musimy się martwić o możliwe kolizje z istniejącymi nazwami w systemie plików.
  • Odłączenie nazwy ścieżki gniazda nie jest konieczne, gdy zakończymy korzystanie z gniazda. Nazwa abstrakcyjna jest automatycznie usuwana po zamknięciu gniazda.
  • Nie musimy tworzyć ścieżki do systemu plików dla gniazda. Może to być przydatne w środowisku chroot lub jeśli nie mamy dostępu do zapisu w systemie plików.

Aby utworzyć powiązanie abstrakcyjne, określamy pierwszy bajt pola sun_path jako bajt zerowy (\ 0). [...]

Uważam, że wraz z odpowiedzią @ user3188445 rozwiązuje to pytanie bardzo dokładnie.

To powiedziawszy, nadal istnieje założenie, że wszystkie procesy, które są SIGKILL, będą miały zamknięte wszystkie otwarte gniazda. To wydaje się rozsądnym założeniem, ale nie mam dokumentacji, która definiowałaby to zachowanie.


1
Ostatni akapit: gniazda są deskryptorami plików, a wszystkie otwarte deskryptory plików są zamykane po zakończeniu procesu. Ish Mówiąc dokładniej, gniazdo jest otwartym plikiem, otwarte pliki można przekazywać np. Dziedziczone przez procesy potomne. Dlatego powinieneś upewnić się, że wywołujesz gniazdo SOCK_CLOEXECna wypadek, gdyby jakikolwiek kod (w tym biblioteka) kiedykolwiek używał fork () + exec (). Tworzenie dodatkowych procesów potomnych za pomocą fork () bez exec () jest mniej powszechne; prawdopodobnie już wiesz, czy to robisz.
sourcejedi

Odłączanie nie jest konieczne ... - um, ponieważ nie ma nazwy ścieżki, nie można odłączyć, a nie tylko niepotrzebne.
domen
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.