Что такое непрерывный процесс?

156

Иногда всякий раз, когда я пишу программу в Linux, и она выходит из строя из-за какой-то ошибки, она становится непрерывным процессом и продолжается до тех пор, пока я не перезагружу свой компьютер (даже если я выйду из системы). Мои вопросы:

  • Что заставляет процесс становиться непрерывным?
  • Как мне остановить это?
  • Вероятно, это глупый вопрос, но есть ли способ прервать его, не перезагружая мой компьютер?
Джейсон Бейкер
источник
Возможно ли, что программа может быть написана для инициирования процесса, который переходит в TASK_UNINTERUPTIBLEсостояние, когда система не находится в состоянии ожидания, тем самым принудительно собирая данные, ожидая передачи после выхода суперпользователя? Это было бы золотой жилой для хакеров, чтобы получать информацию, возвращаться в состояние зомби и передавать информацию по сети в режиме ожидания. Некоторые могут утверждать, что это один из способов создать Blackdoorдля сил, которые могут входить и выходить из любой системы по желанию. Я твердо верю, что эту лазейку можно закрыть навсегда, исключив `TASK_UNINTERUPTIB
Nuuwski
2
пожалуйста поделитесь кодом?
снова

Ответы:

198

Непрерывный процесс - это процесс, который происходит в системном вызове (функция ядра) и не может быть прерван сигналом.

Чтобы понять, что это значит, вам нужно понять концепцию прерываемого системного вызова. Классический пример read(). Это системный вызов, который может занять много времени (секунд), поскольку он может включать в себя раскрутку жесткого диска или перемещение головок. В течение большей части этого времени процесс будет находиться в спящем режиме, блокируясь на оборудовании.

Пока процесс спит в системном вызове, он может получить асинхронный сигнал Unix (скажем, SIGTERM), тогда происходит следующее:

  • Системные вызовы завершаются преждевременно и настроены на возврат -EINTR в пространство пользователя.
  • Обработчик сигнала выполнен.
  • Если процесс все еще выполняется, он получает возвращаемое значение из системного вызова и может повторить тот же вызов.

Ранний возврат из системного вызова позволяет коду пользовательского пространства немедленно изменить свое поведение в ответ на сигнал. Например, завершается чисто в ответ на SIGINT или SIGTERM.

С другой стороны, некоторые системные вызовы не могут быть прерваны таким образом. Если система по какой-либо причине вызывает остановку, процесс может оставаться в этом состоянии до бесконечности.

LWN опубликовала хорошую статью, которая затрагивала эту тему в июле.

Чтобы ответить на оригинальный вопрос:

  • Как предотвратить это: выясните, какой драйвер доставляет вам неприятности, и либо прекратите использование, либо станьте хакером ядра и исправьте его.

  • Как убить непрерывный процесс без перезагрузки: как-то заставить системный вызов завершиться. Часто наиболее эффективный способ сделать это, не нажимая на выключатель питания, это тянуть за шнур питания. Вы также можете стать хакером ядра и заставить драйвер использовать TASK_KILLABLE, как описано в статье LWN.

ddaa
источник
31
Я вытащил шнур питания на моем ноутбуке, и он не работает, к сожалению. ;-)
thecarpy
1
Разве это не EINTR вместо EAGAIN? Также read () возвращает -1 и errno устанавливается на ошибку.
летчик
2
@Dexter: Вы действительно упускаете суть. Прочитайте статью LWN: lwn.net/Articles/288056 . Эти проблемы вызваны ленивыми программистами драйверов устройств, и они должны быть исправлены в коде драйвера устройства.
2012 г.
4
@ddaa "Традиция Unix (и, следовательно, почти всех приложений) полагает, что записи в хранилище файлов не прерываются по сигналу. Изменить эту гарантию было бы небезопасно или практично." -> Это как раз самая неправильная часть всего этого ИМО. Просто прервите запрос на чтение / запись драйвера, и когда фактическое устройство (жесткий диск / сетевая карта / т. Д.) Доставит данные, проигнорируйте их. Ядро ОС должно быть сделано таким образом, чтобы НИ ОДИН разработчик не мог его испортить.
Декстер
2
@ddaa Я знаю, что Linux не является микроядром, хотя я не уверен, какая часть моего комментария относится к нему ... А затем, означает ли ваш комментарий, что у ОС микроядра нет проблем с этими "бесперебойными" процессами? Потому что, если этого не произойдет, может быть, мне пора стать поклонником микроядра ...: D
Декстер
49

Когда процесс находится в режиме пользователя, он может быть прерван в любое время (переключение в режим ядра). Когда ядро ​​возвращается в пользовательский режим, оно проверяет, есть ли какие-либо ожидающие сигналы (включая те, которые используются для остановки процесса, такие как SIGTERMи SIGKILL). Это означает, что процесс может быть остановлен только при возврате в режим пользователя.

Причина, по которой процесс не может быть уничтожен в режиме ядра, заключается в том, что он потенциально может повредить структуры ядра, используемые всеми другими процессами на той же машине (точно так же, как уничтожение потока может потенциально повредить структуры данных, используемые другими потоками в том же процессе) ,

Когда ядру необходимо выполнить что-то, что может занять много времени (например, в ожидании канала, написанного другим процессом или в ожидании аппаратного обеспечения что-либо сделать), оно спит, помечая себя как спящего и вызывая планировщик для переключения на другое процесс (если нет не спящего процесса, он переключается на «фиктивный» процесс, который указывает процессору немного замедляться и сидит в цикле - в цикле простоя).

Если сигнал отправляется в спящий процесс, его необходимо разбудить, прежде чем он вернется в пользовательское пространство и обработает ожидающий сигнал. Здесь у нас есть разница между двумя основными типами сна:

  • TASK_INTERRUPTIBLEПрерванный сон. Если задание помечено этим флагом, оно спит, но может быть разбужено сигналами. Это означает, что код, пометивший задачу как спящий, ожидает возможного сигнала, а после его запуска проверит его и вернется из системного вызова. После обработки сигнала системный вызов может быть автоматически перезапущен (и я не буду вдаваться в подробности о том, как это работает).
  • TASK_UNINTERRUPTIBLEНепрерывный сон. Если задача помечена этим флагом, она не ожидает, что ее разбудит что-либо, кроме того, что она ожидает, либо потому, что ее нелегко перезапустить, либо потому, что программы ожидают, что системный вызов будет атомарным. Это может также использоваться для снов, которые, как известно, очень короткие.

TASK_KILLABLE (упоминается в статье LWN, на которую ссылается ответ ddaa) - это новый вариант.

Это отвечает на ваш первый вопрос. Что касается вашего второго вопроса: вы не можете избежать непрерывных снов, они нормальные (это происходит, например, каждый раз, когда процесс читает / пишет с / на диск); тем не менее, они должны длиться лишь доли секунды. Если они длятся намного дольше, это обычно означает проблему с оборудованием (или проблему с драйвером устройства, которая выглядит так же для ядра), когда драйвер устройства ждет, пока оборудование сделает что-то, что никогда не произойдет. Это также может означать, что вы используете NFS, а сервер NFS не работает (он ожидает восстановления сервера; вы также можете использовать опцию «intr», чтобы избежать проблемы).

Наконец, причина, по которой вы не можете восстановить, - это та же причина, по которой ядро ​​ожидает возврата в режим пользователя, чтобы доставить сигнал или убить процесс: это может привести к повреждению структур данных ядра (код, ожидающий прерывистого сна, может получить ошибку, сообщающую ему вернуться в пользовательское пространство, где процесс может быть убит; код, ожидающий непрерывного сна, не ожидает ошибки).

CesarB
источник
1
Ошибка блокировки файловой системы также является вероятной причиной, IME.
Тобу
3
Я не понимаю всего этого. «Вы не можете избежать бесперебойного сна» - разве ОС не может быть создана таким образом, чтобы бесперебойный сон просто не существовал как состояние? Затем часть о повреждении - нельзя ли завершить часть режима самого ядра (или то, что МОЖЕТ вызвать повреждение) или просто изменить его код прямо в памяти, чтобы просто вернуть? Пожалуйста, объясните, почему это так трудно / невозможно сделать, что даже Linux этого не сделал. (Я думал, что эта проблема существует только на Windows)
Декстер
Единственный случай, который я могу придумать, сделал бы (безопасно) уничтожение этих процессов действительно невозможным (и не только, скажем, исключительно сложным), если бы само оборудование могло вызвать повреждение. Аппаратное обеспечение невозможно контролировать; ядро может . Но именно ядро ​​получает данные от оборудования и модифицирует память (поэтому их нельзя освобождать до того, как процесс вернется в режим пользователя и почему может произойти повреждение) ... измените код ядра в памяти и больше никаких проблем.
Декстер
@Dexter думает о ядре так, как будто это один многопоточный процесс, где часть режима ядра каждого процесса является потоком внутри ядра. Ваше предложение было бы так же плохо, как уничтожение одного потока в многопоточной программе: оно может оставить висячие блокировки, структуры данных, временно измененные или находящиеся в процессе модификации, и так далее.
CesarB
@ CesarB хорошо, вы правы насчет уничтожения потока ... Но разве "основной" поток (например, ядро ​​ОС и другие потоки могут быть драйверами) как-то не справится? Хотя эти структуры «в процессе модификации» кажутся одной очень сложной проблемой ... может быть, мы действительно никогда не увидим ОС, в которой бесперебойные процессы были бы невозможны :(
Dexter
23

Непрерывные процессы обычно ожидают ввода-вывода после сбоя страницы.

Учти это:

  • Поток пытается получить доступ к странице, которая не находится в ядре (или исполняемый файл, который загружен по требованию, страница анонимной памяти, которая была выгружена, или файл mmap () d, который загружен по требованию, которые во многом то же самое)
  • Ядро сейчас (пытается) загрузить его в
  • Процесс не может продолжаться, пока страница не станет доступной.

Процесс / задача не могут быть прерваны в этом состоянии, потому что он не может обрабатывать какие-либо сигналы; если это произойдет, произойдет сбой другой страницы, и он вернется туда, где он был.

Когда я говорю «процесс», я действительно имею в виду «задачу», которая в Linux (2.6) примерно переводится как «нить», которая может иметь или не иметь отдельную запись «группы потоков» в / proc

В некоторых случаях это может ждать долго. Типичным примером этого является случай, когда исполняемый файл или файл mmap'd находятся в сетевой файловой системе, где произошел сбой сервера. Если ввод / вывод в конце концов завершится успешно, задача продолжится. Если это в конечном счете терпит неудачу, задача обычно получает SIGBUS или кое-что.

MarkR
источник
1
Если это в конечном счете терпит неудачу, задача обычно получает SIGBUS или кое-что. Подождите, нельзя ли сделать ядро ​​таким образом, чтобы при уничтожении этих «бесперебойных» процессов оно просто СКАЖИЛО им, что операция ввода / вывода завершилась неудачно? Тогда процесс вернется в режим пользователя и исчезнет? Должен быть способ безопасно убить эти процессы состояния "D". Я думаю, это просто не легко, и поэтому ни Windows, ни Linux пока не имеют такой возможности. С другой стороны, я бы хотел убить эти процессы хотя бы небезопасно. Меня не волнует возможный сбой системы или что-то еще ...
Декстер
@Dexter Хм, я никогда не сталкивался с этой проблемой в Windows. Какой способ воспроизвести это там? По крайней мере, согласно этому посту , все запросы ввода-вывода могут быть прерваны в Windows.
Руслан
1

На ваш третий вопрос: я думаю, что вы можете убить непрерывные процессы, запустив sudo kill -HUP 1. Он перезапустит init без остановки запущенных процессов, и после его запуска мои бесперебойные процессы исчезли.

Рон Грейнджер
источник
-3

Если вы говорите о процессе «зомби» (который обозначен как «зомби» в выводе ps), то это безвредная запись в списке процессов, ожидающая, когда кто-то соберет код возврата, и ее можно было бы безопасно проигнорировать.

Не могли бы вы описать, что и для вас означает "непрерывный процесс"? Выживает ли он после «убить -9» и радостно пыхтит? Если это так, то он застрял на каком-то системном вызове, который застрял в каком-то драйвере, и вы застряли с этим процессом до перезагрузки (а иногда лучше перезагрузить в ближайшее время) или выгрузки соответствующего драйвера (что вряд ли произойдет) , Вы можете попытаться использовать «strace», чтобы узнать, где застрял ваш процесс, и избежать этого в будущем.

искусный
источник
Разве нельзя принудительно выгружать драйверы так же, как процесс может быть убит? Я знаю, что режим ядра имеет более привилегированный доступ, чем режим пользователя, но он никогда не может быть более привилегированным, чем сама операционная система. Все, что выполняется в режиме ядра, всегда может повлиять на что-либо еще, выполняемое в режиме ядра - контроль просто отсутствует.
Декстер