Linux как посмотреть код завершения процесса

Глава 6. Завершение и код завершения

. эта часть Bourne shell покрыта мраком, тем не менее все пользуются ею.

Chet Ramey

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

Каждая команда возвращает код завершения (иногда код завершения называют возвращаемым значением ). В случае успеха команда должна возвращать 0 , а в случае ошибки — ненулевое значение, которое, как правило, интерпретируется как код ошибки. Практически все команды и утилиты UNIX возвращают 0 в случае успешного завершения, но имеются и исключения из правил.

Аналогичным образом ведут себя функции, расположенные внутри сценария, и сам сценарий, возвращая код завершения. Код, возвращаемый функцией или сценарием, определяется кодом возврата последней команды. Команде exit можно явно указать код возврата, в виде: exit nnn , где nnn — это код возврата (число в диапазоне 0 — 255 ).

Когда работа сценария завершается командой exit без параметров, то код возврата сценария определяется кодом возврата последней исполненной командой.

Код возврата последней команды хранится в специальной переменной $?. После исполнения кода функции, переменная $? хранит код завершения последней команды, исполненной в функции. Таким способом в Bash передается «значение, возвращаемое» функцией. После завершения работы сценария, код возврата можно получить, обратившись из командной строки к переменной $?, т.е. это будет код возврата последней команды, исполненной в сценарии.

Пример 6-1. завершение / код завершения

Переменная $? особенно полезна, когда необходимо проверить результат исполнения команды (см. Пример 12-27 и Пример 12-13).

Символ !, может выступать как логическое «НЕ» для инверсии кода возврата.

Пример 6-2. Использование символа ! для логической инверсии кода возврата

В отдельных случаях коды возврата должны иметь предопределенные значения и не должны задаваться пользователем.

Источник

Как определить статус завершения скрипта или команды в Linux

Хотите писать навороченные сценарии и стать экспертом в написании скриптов оболочки? Тогда этот пост для вас 😉

В Linux/Unix когда вы запускаете скрипт или команду, они завершатся со значимым статусом для того чтобы вы поняли — достигли ли вы своей цели запуская их или нет. Тем самым мы можем предпринять какие-то действия по статусу завершения (выполнено, провалено или выполнено частично) этих команд. В linux есть некоторые команды, которые не отображают статус завершения, например команда «mount -a» которую мы выполняем после правки файла fstab не покажет вам ничего. Так как же мы узнаем что команда отработала и на сколько успешно? Для этого в Linux/Unix есть встроенная переменная которая хранит статус завершения для любой команды или запущенного скрипта. Этот статус хранится во встроенной переменно «$?». Статус завершения принимает значение от 0 до 255. Некоторые из обычно используемых статусов завершения приведены ниже.

Несколько примеров того, как использовать статусы завершения.

Как можно использовать статус завершения для проверки команды? Запустите команду которую хотите проверить, затем выполните команду

для проверки статус выполнения команды.

Пример 1:

Если вы получили статус завершения равный нулю, то команды была выполнена успешно.

Как можно использовать статус завершения в сценариях оболочки?

Источник

Код завершения программ. Несколько вопросов!

В Linux есть возможность проверить (в консоли), с каким кодом завершилась программа.

Обычно 0 (true) означает корректное завершение, а 1 (false) — с ошибками. Но прочитал, что это не обязательно всегда так. Типа это общепринятые обозначения в программировании, и их надо придерживаться, но коды могут быть разными. А зачем нужно что-то кроме 0 и 1? Какие еще состояния программы бывают, кроме «корректно завершилась» или «вышла с ошибками»? Можно ли тут в пример привести разные Windows-программы, которые иногда падают и вылезают ошибки вида «0x1234» что-то такое, или это не то? Не совсем понятно, о чем такие ошибки могут сказать пользователю.

Собственно, прочел, что и в баш-скрипте можно самому задать код завершения в нужном месте, например, так.

И потом проверить его с помощью echo $? (по крайне мере, первый вызов этой штуки после завершения скрипта точно сработает. потом будет почему-то всегда 0 показывать).

Но заметил такую штуку, что если записать определенную команду в if, и заставить выполнить какое-то действие по завершению команды.

То не всегда прокатывает, в отличии от голой консоли! Тут проблема в том, что походу (поправьте, если неправ) не все консольные программы в Linux возвращают true (0) или false (1) в качестве «последнего» вывода. не знаю как назвать короче. В общем есть команды, которые ничего не выводят на экран. У таких прокатывает фишка с получением кода возврата в конструкции if. А другие просто выкидывают вывод результатов выполнения в stdout, тогда через if непонятно как получить код возврата. Ведь оно возвращает ненужный результат выполнения вместо кода возврата.

И еще, как перевести на русский определение из мануала? Гугл какую-то нелепицу выдает.

($?) Expands to the exit status of the most recently executed foreground pipeline.

Источник

Популярный Linux

Вход в систему

Новые записи в блогах

Популярное

За последнее время:

стрижка собак недорого salon-milord.ru

Глава 6. Завершение и код завершения

. эта часть Bourne shell покрыта мраком, тем не менее все пользуются ею.

Chet Ramey

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

Каждая команда возвращает код завершения (иногда код завершения называют возвращаемым значением ). В случае успеха команда должна возвращать 0 , а в случае ошибки — ненулевое значение, которое, как правило, интерпретируется как код ошибки. Практически все команды и утилиты Unix возвращают 0 в случае успешного завершения, но имеются и исключения из правил.

Аналогичным образом ведут себя функции, расположенные внутри сценария, и сам сценарий, возвращая код завершения. Код, возвращаемый функцией или сценарием, определяется кодом возврата последней команды. Команде exit можно явно указать код возврата, в виде: exit nnn , где nnn — это код возврата (число в диапазоне 0 — 255 ).

Когда работа сценария завершается командой exit без параметров, то код возврата сценария определяется кодом завершения последней исполненной команды (не считая саму команду exit).

Источник

Изучаем процессы в Linux


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

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

Всё написанное ниже справедливо к Debian Linux с ядром 4.15.0.

Содержание

Введение

Системное программное обеспечение взаимодействует с ядром системы посредством специальных функций — системных вызовов. В редких случаях существует альтернативный API, например, procfs или sysfs, выполненные в виде виртуальных файловых систем.

Атрибуты процесса

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

  • Идентификатор процесса (pid)
  • Открытые файловые дескрипторы (fd)
  • Обработчики сигналов (signal handler)
  • Текущий рабочий каталог (cwd)
  • Переменные окружения (environ)
  • Код возврата

Жизненный цикл процесса

Рождение процесса

Только один процесс в системе рождается особенным способом — init — он порождается непосредственно ядром. Все остальные процессы появляются путём дублирования текущего процесса с помощью системного вызова fork(2) . После выполнения fork(2) получаем два практически идентичных процесса за исключением следующих пунктов:

  1. fork(2) возвращает родителю PID ребёнка, ребёнку возвращается 0;
  2. У ребёнка меняется PPID (Parent Process Id) на PID родителя.

После выполнения fork(2) все ресурсы дочернего процесса — это копия ресурсов родителя. Копировать процесс со всеми выделенными страницами памяти — дело дорогое, поэтому в ядре Linux используется технология Copy-On-Write.
Все страницы памяти родителя помечаются как read-only и становятся доступны и родителю, и ребёнку. Как только один из процессов изменяет данные на определённой странице, эта страница не изменяется, а копируется и изменяется уже копия. Оригинал при этом «отвязывается» от данного процесса. Как только read-only оригинал остаётся «привязанным» к одному процессу, странице вновь назначается статус read-write.

Состояние «готов»

Сразу после выполнения fork(2) переходит в состояние «готов».
Фактически, процесс стоит в очереди и ждёт, когда планировщик (scheduler) в ядре даст процессу выполняться на процессоре.

Состояние «выполняется»

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

Перерождение в другую программу

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

или библиотечным вызовам execl(3), execlp(3), execle(3), execv(3), execvp(3), execvpe(3) :

Все из перечисленных вызовов выполняют программу, путь до которой указан в первом аргументе. В случае успеха управление передаётся загруженной программе и в исходную уже не возвращается. При этом у загруженной программы остаются все поля структуры процесса, кроме файловых дескрипторов, помеченных как O_CLOEXEC , они закроются.

Как не путаться во всех этих вызовах и выбирать нужный? Достаточно постичь логику именования:

  • Все вызовы начинаются с exec
  • Пятая буква определяет вид передачи аргументов:
    • l обозначает list, все параметры передаются как arg1, arg2, . NULL
    • v обозначает vector, все параметры передаются в нуль-терминированном массиве;
  • Далее может следовать буква p, которая обозначает path. Если аргумент file начинается с символа, отличного от «/», то указанный file ищется в каталогах, перечисленных в переменной окружения PATH
  • Последней может быть буква e, обозначающая environ. В таких вызовах последним аргументом идёт нуль-терминированный массив нуль-терминированных строк вида key=value — переменные окружения, которые будут переданы новой программе.

Семейство вызовов exec* позволяет запускать скрипты с правами на исполнение и начинающиеся с последовательности шебанг (#!).

Есть соглашение, которое подразумевает, что argv[0] совпадает с нулевым аргументов для функций семейства exec*. Однако, это можно нарушить.

Любопытный читатель может заметить, что в сигнатуре функции int main(int argc, char* argv[]) есть число — количество аргументов, но в семействе функций exec* ничего такого не передаётся. Почему? Потому что при запуске программы управление передаётся не сразу в main. Перед этим выполняются некоторые действия, определённые glibc, в том числе подсчёт argc.

Состояние «ожидает»

Некоторые системные вызовы могут выполняться долго, например, ввод-вывод. В таких случаях процесс переходит в состояние «ожидает». Как только системный вызов будет выполнен, ядро переведёт процесс в состояние «готов».
В Linux так же существует состояние «ожидает», в котором процесс не реагирует на сигналы прерывания. В этом состоянии процесс становится «неубиваемым», а все пришедшие сигналы встают в очередь до тех пор, пока процесс не выйдет из этого состояния.
Ядро само выбирает, в какое из состояний перевести процесс. Чаще всего в состояние «ожидает (без прерываний)» попадают процессы, которые запрашивают ввод-вывод. Особенно заметно это при использовании удалённого диска (NFS) с не очень быстрым интернетом.

Состояние «остановлен»

В любой момент можно приостановить выполнение процесса, отправив ему сигнал SIGSTOP. Процесс перейдёт в состояние «остановлен» и будет находиться там до тех пор, пока ему не придёт сигнал продолжать работу (SIGCONT) или умереть (SIGKILL). Остальные сигналы будут поставлены в очередь.

Завершение процесса

Ни одна программа не умеет завершаться сама. Они могут лишь попросить систему об этом с помощью системного вызова _exit или быть завершенными системой из-за ошибки. Даже когда возвращаешь число из main() , всё равно неявно вызывается _exit .
Хотя аргумент системного вызова принимает значение типа int, в качестве кода возврата берется лишь младший байт числа.

Состояние «зомби»

Сразу после того, как процесс завершился (неважно, корректно или нет), ядро записывает информацию о том, как завершился процесс и переводит его в состояние «зомби». Иными словами, зомби — это завершившийся процесс, но память о нём всё ещё хранится в ядре.
Более того, это второе состояние, в котором процесс может смело игнорировать сигнал SIGKILL, ведь что мертво не может умереть ещё раз.

Забытье

Код возврата и причина завершения процесса всё ещё хранится в ядре и её нужно оттуда забрать. Для этого можно воспользоваться соответствующими системными вызовами:

Вся информация о завершении процесса влезает в тип данных int. Для получения кода возврата и причины завершения программы используются макросы, описанные в man-странице waitpid(2) .

Передача argv[0] как NULL приводит к падению.

Бывают случаи, при которых родитель завершается раньше, чем ребёнок. В таких случаях родителем ребёнка станет init и он применит вызов wait(2) , когда придёт время.

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

Благодарности

Спасибо Саше «Al» за редактуру и помощь в оформлении;

Спасибо Саше «Reisse» за понятные ответы на сложные вопросы.

Они стойко перенесли напавшее на меня вдохновение и напавший на них шквал моих вопросов.

Источник

Читайте также:  Индикатор службы microsoft windows search windows 10 грузит диск
Оцените статью