Когда процесс находится в пользовательском режиме, он может быть прерван в любое время (переключение в режим ядра). Когда ядро возвращается в режим пользователя, оно проверяет, есть ли какие-либо ожидающие сигналы (включая те, которые используются для прекращения процесса, такие как SIGTERM
и SIGKILL
). Это означает, что процесс может быть остановлен только при возвращении в режим пользователя.
Причина, по которой процесс не может быть остановлен в режиме ядра, заключается в том, что он потенциально может повредить структуры ядра, используемые всеми другими процессами на той же машине (точно так же, как уничтожение потока может потенциально повредить структуры данных, используемые другими потоками в тот же процесс).
Когда ядру нужно сделать что-то, что может занять много времени (например, в ожидании канала, написанного другим процессом или в ожидании аппаратного обеспечения что-то сделать), оно спит, помечая себя как спящего и вызывая планировщик для переключиться на другой процесс (если нет не спящего процесса, он переключается на «фиктивный» процесс, который говорит процессору немного замедлить работу и находится в цикле - цикл ожидания).
Если сигнал отправляется в спящий процесс, его необходимо разбудить, прежде чем он вернется в пространство пользователя и, таким образом, обработает ожидающий сигнал. Здесь у нас есть разница между двумя основными типами сна:
TASK_INTERRUPTIBLE
, прерванный сон. Если задача помечена этим флагом, она спит, но может быть разбужена сигналами. Это означает, что код, который пометил задачу как спящий, ожидает возможного сигнала, а после его запуска проверит его и вернется из системного вызова. После обработки сигнала системный вызов может быть автоматически перезапущен (и я не буду вдаваться в подробности о том, как это работает).
TASK_UNINTERRUPTIBLE
, непрерывный сон. Если задача помечена этим флагом, она не ожидает, что ее разбудит что-либо, кроме того, что она ожидает, либо потому, что ее нелегко перезапустить, либо потому, что программы ожидают, что системный вызов будет атомарным. Это может также использоваться для очень коротких снов.
TASK_KILLABLE
(упоминается в статье LWN, на которую ссылается ответ ddaa) - новый вариант.
Это отвечает на ваш первый вопрос. Что касается вашего второго вопроса: вы не можете избежать непрерывных снов, они нормальные (это происходит, например, каждый раз, когда процесс читает / пишет с / на диск); однако, они должны длиться только доли секунды. Если они длятся намного дольше, это обычно означает проблему с оборудованием (или проблему с драйвером устройства, которая выглядит так же для ядра), когда драйвер устройства ждет, пока оборудование сделает что-то, что никогда не произойдет. Это также может означать, что вы используете NFS, а сервер NFS не работает (он ожидает восстановления сервера; вы также можете использовать опцию «intr», чтобы избежать проблемы).
Наконец, причина, по которой вы не можете восстановить, - это та же причина, по которой ядро ждет, пока не вернется в пользовательский режим, чтобы доставить сигнал или завершить процесс: это может привести к повреждению структур данных ядра (код, ожидающий прерывистого сна, может получить ошибку что говорит ему вернуться в пользовательское пространство, где процесс может быть остановлен; код, ожидающий в непрерывном режиме сна, не ожидает никакой ошибки).