Вопрос или проблема
Используя Docker, я недавно обнаружил, что PID 1 по умолчанию нельзя убить с помощью SIGTERM
. Однако вы все равно можете поймать SIGTERM
в PID 1, если настроите обработчик в процессе. Таким образом, это не похоже на то, что ядро просто маскирует SIGTERM
для PID 1.
Как это реализовано? Как процесс с PID 1 имеет обработчик SIGTERM
по умолчанию, который является no-op?
Исследуя исходный код ядра, похоже, что SIG_DFL
на самом деле просто значение 0
и не указывает на какой-либо “стандартный” код, который мог бы поступать из libc. То же самое с SIG_IGN
— его значение 1
.
Таким образом, стандартный обработчик на самом деле находится в пространстве ядра, а не в пространстве пользователя. Значит, у ядра должен быть специальный случай для PID 1 в этом стандартном обработчике.
Ссылки:
Это может быть флагом, получаемым всеми сигналами 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, рекомендуется ознакомиться с исходным кодом ядра и специальными страницами документации вашего дистрибутива.