Чи автоматично Linux очищає абстрактні розетки домену?


15

У StackOverflow є чудова відповідь про забезпечення кращого блокування для демонів (синтезованих від Едуардо Флері ), що не залежить від загального механізму блокування файлів PID для демонів. Там є багато хороших коментарів щодо того, чому файли блокування PID іноді можуть викликати проблеми, тому я не буду їх повторно переробляти тут.

Коротше кажучи, рішення покладається на абстрактні сокети домену простору імен Linux, які відслідковують сокети за назвою для вас, а не покладаються на файли, які можуть зберігатися після того, як демон буде SIGKILL'd. Приклад показує, що, здається, Linux звільняє сокет, коли процес загине.

Але я не можу знайти остаточну документацію в Linux, яка говорить про те, що саме Linux робить з абстрактним сокетом, коли пов'язаний процес є SIGKILL'd. Хтось знає?

По-іншому, коли саме абстрактна розетка буде звільнена для використання знову?

Я не хочу замінювати механізм файлів PID абстрактними сокетами, якщо це остаточно не вирішить проблему.


3
Я не можу знайти нічого, що би відповіло на це безпосередньо. Але оскільки не існує API для видалення абстрактних сокетів, схоже, ядром їм доведеться керувати автоматично. Коли немає розкритого розетки, він повинен пройти.
Вармар

@Barmar Ярмарок досить. Хочете додати це як відповідь?
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.out, і ss -axпокаже, розетка пішла.

Однак причина, за якою ви не можете знайти цю очистку в жодній документації, полягає в тому, що вона не є дійсно очищенням у тому ж сенсі, що необмежені розетки unix-домену потребують очищення. Неабстрактний сокет фактично виділяє inode і створює запис у каталозі, який потрібно очистити в базовій файловій системі. Навпаки, подумайте про абстрактний сокет більше, ніж номер порту TCP або UDP. Звичайно, якщо ви прив’яжете порт TCP, а потім вийти, той порт TCP знову буде вільним. Але яке б 16-бітове число ви не використовували, все ще існує абстрактно і завжди. Простір імен номерів портів становить 1-65535 і ніколи не змінюється та не потребує очищення.

Тому просто подумайте про абстрактне ім'я сокета, як номер порту TCP або UDP, щойно вибране з набагато більшого набору можливих номерів портів, які, схоже, імена шляхів, але не є. Ви не можете прив’язати один і той же номер порту двічі (заборона SO_REUSEADDRабо SO_REUSEPORT). Але закриття сокета (явно або неявно шляхом припинення) звільняє порт, не залишаючи нічого для очищення.


Це вже пройшло випробування на качку. Це добре для деяких речей, наприклад, python, але я очікую більше для функцій ядра Linux. Візьміть ваші приклади портів TCP / UDP - є багато документації, яка точно визначає, коли порт можна використовувати повторно.
CivFan

2
І чому ця документація не застосовується однаково до абстрактних розеток? У вас є довідник? Краще питання може бути, де додаткове ускладнення очищення для не абстрактних розеток Unix-домену задокументовано. У моїй системі це в unix (7) , де сказано, що "Linux також підтримує абстрактний простір імен, незалежний від файлової системи". Тож для мене "незалежна від файлової системи" не передбачає очищення від файлової системи.
користувач3188445

5

Я опублікував це питання понад рік тому і ніколи не був задоволений відсутністю остаточної документації. Я думав, що ще раз перевірю документацію Linux на наявність оновлень, і радий це побачити :

Анотація розетки

Дозволи на сокет не мають значення для абстрактних сокетів: процес umask (2) не робить ефекту при прив'язці абстрактного сокета, а зміна власності та прав на об'єкт (через fchown (2) та fchmod (2)) не впливає на доступність розетки.

Абстрактні розетки автоматично зникають, коли закриті всі відкриті посилання на сокет.

Також, в Linux Програмування інтерфейсу по Майклу Керріск охоплює питання (крос відправлений від цього іншої відповіді ):

57.6 Простір імен абстрактних сокетів Linux

Так званий абстрактний простір імен - це особливість Linux, яка дозволяє нам прив’язувати сокет домену UNIX до імені, не створюючи це ім’я у файловій системі. Це забезпечує кілька потенційних переваг:

  • Нам не потрібно турбуватися про можливі зіткнення з існуючими іменами у файловій системі.
  • Не потрібно від’єднувати імена шляху сокета, коли ми закінчили використовувати сокет. Анотаційне ім’я автоматично видаляється, коли розетка закрита.
  • Нам не потрібно створювати ім'я файлової системи для сокета. Це може бути корисно в середовищі chroot або якщо у нас немає доступу для запису до файлової системи.

Для створення абстрактного прив’язки ми вказуємо перший байт sun_path поля як нульовий байт (\ 0). [...]

Я вважаю, що поряд із відповіддю @ user3188445 це дуже чітко записує це питання.

Однак, тут все ще припущення, що процеси, які є SIGKILL, закриють усі відкриті сокети. Це здається розумним припущенням, але я не маю документації, яка б визначала цю поведінку.


1
Останній абзац: сокети - це дескриптори файлів, а всі відкриті дескриптори файлів закриваються, коли процес закінчується. Іш. Більш конкретно, сокет - це відкритий файл, відкриті файли можуть передаватися навколо, наприклад, успадковані дочірніми процесами. Тому вам слід переконатися в тому, щоб зателефонувати в сокет, SOCK_CLOEXECякщо будь-який код (включаючи бібліотеку) коли-небудь робить fork () + exec (). Створення додаткових дочірніх процесів за допомогою fork () без exec () рідше; ви, мабуть, вже знаєте, чи робите ви це.
sourcejedi

Не потрібно від’єднувати зв’язок ... - гм, оскільки немає імені шляху, від’єднати його неможливо, не просто зайве.
Domen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.