Linux автоматически очищает абстрактные доменные сокеты?

15

На StackOverflow есть отличный ответ о предоставлении лучшей блокировки для демонов (синтезированных из Eduardo Fleury ), которая не зависит от общего механизма блокировки файлов PID для демонов. Там есть много хороших комментариев о том, почему файлы блокировки PID могут иногда вызывать проблемы, поэтому я не буду перефразировать их здесь.

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

Но я не могу найти в Linux исчерпывающую документацию, в которой говорится, что именно Linux делает с абстрактным сокетом, когда связанный процесс выполняется SIGKILL. Кто-нибудь знает?

Иными словами, когда точно освободится абстрактный сокет для повторного использования?

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

CivFan
источник
3
Я не могу найти ничего, что отвечает на это напрямую. Но поскольку нет API для удаления абстрактных сокетов, похоже, что ядром им нужно будет управлять автоматически. Когда нет процессов с открытым сокетом, он должен исчезнуть.
Бармар
@ Ярмарка Бармара достаточно. Хотите добавить это как ответ?
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 требуют очистки. Неабстрактный сокет фактически выделяет индекс и создает запись в каталоге, которую необходимо очистить в базовой файловой системе. Напротив, думайте об абстрактном сокете больше как номер порта TCP или UDP. Конечно, если вы связываете порт TCP и затем выходите, этот порт TCP снова будет свободен. Но какой бы 16-битный номер вы ни использовали, он существует абстрактно и всегда существует. Пространство имен номеров портов - 1-65535 и никогда не изменяется и не нуждается в очистке.

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

user3188445
источник
Это уже прошло тест на утку. Это хорошо для некоторых вещей, таких как python, но я ожидаю большего от возможностей ядра Linux. Возьмите свои примеры портов TCP / UDP - есть много документации, описывающей, когда именно порт можно использовать повторно.
CivFan
2
И почему эта документация в равной степени не относится к абстрактным сокетам? У вас есть ссылка? Лучше задать вопрос, где задокументирована дополнительная сложность очистки для неабстрактных сокетов домена Unix. В моей системе, это в UNIX (7) , в котором говорится , «Linux также поддерживает абстрактное пространство имен , которое не зависит от файловой системы.» Таким образом, для меня «независимость от файловой системы» подразумевает отсутствие очистки, специфичной для файловой системы.
user3188445
5

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

Абстрактные розетки

Разрешения сокетов не имеют смысла для абстрактных сокетов: процесс umask (2) не действует при связывании абстрактного сокета, а изменение владельца и разрешений объекта (через fchown (2) и fchmod (2)) не влияет на доступность розетки.

Абстрактные сокеты автоматически исчезают, когда все открытые ссылки на сокет закрыты.

Кроме того , в Linux Программирование интерфейса по Майклу Керриск охватывает вопрос (кросс отправленный от этого другого ответа ):

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

Так называемое абстрактное пространство имен - это специфическая особенность Linux, которая позволяет нам привязывать сокет домена UNIX к имени без создания этого имени в файловой системе. Это обеспечивает несколько потенциальных преимуществ:

  • Нам не нужно беспокоиться о возможных конфликтах с существующими именами в файловой системе.
  • Нет необходимости отсоединять путь к сокету, когда мы закончили использовать сокет. Абстрактное имя автоматически удаляется при закрытии сокета.
  • Нам не нужно создавать путь к файловой системе для сокета. Это может быть полезно в среде chroot или если у нас нет прав на запись в файловую систему.

Чтобы создать абстрактную привязку, мы указываем первый байт поля sun_path как нулевой байт (\ 0). [...]

Я считаю, что вместе с ответом @ user3188445 это очень точно проясняет вопрос.

Тем не менее, здесь все еще делается предположение, что для процессов, которые являются SIGKILL, все открытые сокеты будут закрыты. Это кажется разумным предположением, но у меня нет документации, определяющей это поведение.

CivFan
источник
1
Последний абзац: сокеты являются файловыми дескрипторами, и все открытые файловые дескрипторы закрываются при выходе из процесса. Иш. В частности, сокет - это открытый файл, открытые файлы могут передаваться, например, наследоваться дочерними процессами. Поэтому вы должны обязательно вызывать socket, SOCK_CLOEXECесли какой-либо код (включая библиотеку) когда-либо выполняет fork () + exec (). Создание дополнительных дочерних процессов с использованием fork () без exec () встречается реже; Вы, наверное, уже знаете, делаете ли вы это.
sourcejedi
Нет необходимости отсоединять ... - гм, так как нет пути к файлу, отсоединение невозможно, не просто ненужно.
Домен