Как PID 1 становится особенным и неуничтожимым?

Вопрос или проблема

Используя Docker, я недавно обнаружил, что PID 1 по умолчанию нельзя убить с помощью SIGTERM. Однако вы все равно можете поймать SIGTERM в PID 1, если настроите обработчик в процессе. Таким образом, это не похоже на то, что ядро просто маскирует SIGTERM для PID 1.

Как это реализовано? Как процесс с PID 1 имеет обработчик SIGTERM по умолчанию, который является no-op?

Исследуя исходный код ядра, похоже, что SIG_DFL на самом деле просто значение 0 и не указывает на какой-либо “стандартный” код, который мог бы поступать из libc. То же самое с SIG_IGN — его значение 1.

Таким образом, стандартный обработчик на самом деле находится в пространстве ядра, а не в пространстве пользователя. Значит, у ядра должен быть специальный случай для PID 1 в этом стандартном обработчике.

Ссылки:

https://stackoverflow.com/questions/32625896/where-is-the-code-for-default-signal-handler-in-elf-binary

Это может быть флагом, получаемым всеми сигналами PID 1:

264  #define SIGNAL_UNKILLABLE   0x00000040 /* для init: игнорировать фатальные сигналы */

И вероятно здесь:

2832  /*
2833   * Глобальный init не получает сигналы, которые он не хочет.
2834   * Контейнерный init не получает сигналы, которые он не хочет из того же
2835   * контейнера.
2836   *
2837   * Обратите внимание, что если глобальный/контейнерный init видит сигналы sig_kernel_only()
2838   * здесь, сигнал должен был быть сгенерирован внутренне
2839   * или должен был поступить из родительского пространства имен. В любом случае,
2840   * сигнал не может быть сброшен.
2841   */
2842  if (unlikely(signal->flags & SIGNAL_UNKILLABLE) &&
2843          !sig_kernel_only(signr))
2844      continue;

он выполняет continue, что не позволяет ему выполнить действие по умолчанию (в случае PID 1). Но я не уверен.

Ответ или решение

Как PID 1 становится особенным и некалируемым в Linux?

В Linux процесс с идентификатором (PID) 1, обычно это init или systemd, обрабатывает сигналы по-разному по сравнению с другими процессами в системе. Если вы когда-либо использовали Docker или работали с контейнерами, вы, вероятно, заметили, что PID 1 не реагирует на сигналы, такие как SIGTERM, тем не менее, он может их обрабатывать, если вы настроите обработчик сигналов в процессе. Это вызывает интересный вопрос: как именно реализована эта особенность и что делает PID 1 "некалируемым"?

Основы работы сигналов в Linux

В Linux сигналы — это механизм, позволяющий процессам обмениваться уведомлениями о событиях. Сигналы могут быть отправлены процесса различными способами, особенно с использованием системных вызовов или из других процессов.

Каждый сигнал имеет "умолчательный" обработчик, который определяет, что происходит, когда процесс получает сигнал. Например, для сигнала SIGTERM умолчательный обработчик убивает процесс.

Перегрузка обработчиков сигналов

Хотя для PID 1 умолчательный обработчик SIGTERM фактически является "no-op" (т.е. ничего не делает), это не результат простой маскировки сигнала только на уровне ядра. PID 1 имеет возможность устанавливать пользовательский обработчик для сигналов, что означает, что его можно настроить для выполнения определённых действий в ответ на получение сигнала, как это было бы сделано для любого другого процесса.

Как это достигается в коде ядра

Когда вы копаете в исходный код ядра Linux, вы найдете следующее определение, относящееся к PID 1:

#define SIGNAL_UNKILLABLE   0x00000040 /* для init: игнорировать фатальные сигналы */

Это определение устанавливает флаг S_SIGNAL_UNKILLABLE для процессов с PID 1. Этот флаг указывает ядру, что процесс (в данном случае PID 1) не должен обрабатывать сигналы, кроме тех, которые он явно ожидает.

Фрагмент кода, который управляет этой логикой, выглядит следующим образом:

if (unlikely(signal->flags & SIGNAL_UNKILLABLE) &&
    !sig_kernel_only(signr))
    continue;

Проверка условиях с использованием флага SIGNAL_UNKILLABLE позволяет процессу игнорировать сигналы, которые ему не нужны. Это означает, что когда процесс PID 1 получает сигнал, который не был явно разрешен, код в ядре просто переходит к следующему сигналу, не выполняя стандартное действие по умолчанию.

Преимущества и использование

Почему же PID 1 стал таким важным и специальным? Во-первых, он отвечает за управление жизненным циклом других процессов. Если бы он неожиданно завершился, это могло бы привести к нестабильности всей системы. Поэтому наличие механизмов, позволяющих игнорировать сигналы, критически важно для обеспечения стабильной работы системы.

Заключение

Таким образом, PID 1 в Linux становится некалируемым не благодаря маскировке сигналов в ядре, а благодаря реализации специального поведения, которое позволяет игнорировать сигналы, если это необходимо. Это достигается с использованием флага SIGNAL_UNKILLABLE, а также механизмов обработки сигналов на уровне ядра. При этом важно помнить, что этот процесс также может настроить собственные обработчики для сигналов, что добавляет гибкости его функциональности.

Для более подробного изучения соответствующих аспектов работы сигналов в Linux, рекомендуется ознакомиться с исходным кодом ядра и специальными страницами документации вашего дистрибутива.

Оцените материал
Добавить комментарий

Капча загружается...