что такое код возврата
Код возврата
При выполнении компьютерных программ операционная система создаёт сущность, называемую процессом, с помощью которой она отслеживает и ведёт учёт выполнения программы. В многозадачных операционных системах, таких как Unix или Linux, новый процесс может быть порождён существующим процессом. Процесс-создатель называется «родительским», а создаваемый процесс — «дочерним». После создания дочерний процесс выполняется параллельно с родительским. Такая техника порождения дочерних процессов используется, чтобы передать часть работы дочернему процессу, в ситуациях, когда родительскому процессу не обязательно тратить на неё время. Когда дочерний процесс завершает своё выполнение, он автоматически либо явно по указанию программиста совершает системный вызов exit, передавая ему целое число. В результате вызова exit целое число передаётся родительскому процессу, который может получить его с помощью системного вызова wait.
Связанные понятия
Сопрограммы (англ. coroutines) — методика связи программных модулей друг с другом по принципу кооперативной многозадачности: модуль приостанавливается в определённой точке, сохраняя полное состояние (включая стек вызовов и счётчик команд), и передаёт управление другому. Тот, в свою очередь, выполняет задачу и передаёт управление обратно, сохраняя свои стек и счётчик.
В теории компиляторов удалением мёртвого кода (англ. dead code elimination, DCE) называется оптимизация, удаляющая мёртвый код. Мёртвым кодом (так же бесполезным кодом) называют код, исполнение которого не влияет на вывод программы, все результаты вычисления такого кода являются мёртвыми переменными, то есть переменными, значения которых в дальнейшем в программе не используются.
Коды возврата & исключения
Замечательные статьи публиковались в последнее время, хотелось бы добавить ещё несколько абзацев по данной теме.
Уважаемые авторы предыдущих топиков как-то упускали тот момент (или мне показалось? или это само-собой подразумевается?) что exceptions возникли как инструмент для решения весьма утилитарной задачи — передачи управления из места возникновения ошибки в то место, где она может быть обработана.
Немного истории, чтобы понятно было, откуда такая задача возникла. В каждом более-менее нетривиальном программном обеспечении (сложнее, чем «Hello, world», да) всегда существуют точки, где нормальное выполнение не может продолжаться — I/O подсистема выдала отказ, памяти для алгоритма почему-то не хватило, входные параметры для функции ей не понравились и т.п. Как именно реагировать?
Ситуация становится ещё печальней, если рассматривать создание какой-либо библиотеки, которая должна будет использоваться в других проектах. Мы не можем (как следствие) вызывать assert/abort или ещё какую-нибудь подобный обработчик — откуда мы знаем, что имеем право завершать работу всего приложения? К примеру, наша библиотека занимается сбором какой-то статистики входных данных, а из-за такого её поведения будет остановлена работа всего устройства. А пишем мы firmware для кардиостимулятора, конечно же.
Ок, abort() — не годится. Мы не хотим создавать приложения, подобные воздушному шарику — в любом месте уколол, весь шарик умер. Мы хотим пользоваться технологией, которая позволит разделить место, где возникла ошибка, и место где принимается решение о том, что именно мы будем делать с этой ошибкой. Так как для одних применений реакцией будет запрет сбора этой статистики, для других — переинициализация библиотеки (например, с другими параметрами), где-то — просто игнорирование. Так как на более высоком уровне доступно гораздо больше информации о том, как реагировать на возникшую ошибку.
Собственно, написано это всё было для того, чтобы лучшее понимание «для чего именно» служат exceptions позволяло лучше понимать «а как их применять и для каких случаях».
PS: ещё было желание показать на примерах кода, какие именно минусы есть у exceptions. И как решалась задача «и ошибки удобно обрабатывать и exceptions при этом не использовать» (актуально для встроенных систем, когда «кардиостимуляторы» пишутся). Но это позже.
Коды возврата vs исключения: взгляд с колокольни
Просмотрев пост Коды возврата vs исключения и комментарии к нему, я заметил, что в обсуждении упущена одна нить, краткий тезис которой следующий: в некоторых языках такая проблема даже не стоИт, т.к. вопрос «что выбрать, коды возврата или исключения» в таком языке является низкоуровневым. Как, например, не стоит вопрос, каким образом реализовать конструкцию «foreach». Т.к. для программиста, использующего тот же «foreach», нет никакой разницы, использовали ли создатели языка while или for или что-то еще в имплементации данного оператора. Главное это паттерн, который представляет собой этот самый оператор.
Посмотрим, они работают.
process-retcode-answer
( defn ok-processor [ result ]
( println ( str «ok. result: » result ) ) )
( defn error-processor [ result ]
( println ( str «error. result: » result ) ) )
( defn another-error-processor [ result ]
( println ( str «another error. result: » result ) ) )
( defn unknown-error-processor [ result ]
( println ( str «unknown error. result: » result ) ) )
Определяем map кодов возврата на наименования обрабатывающих их функций:
( def result-mapping < 0 'ok-processor
— 1 ‘error-processor
— 2 ‘another-error-processor
: other ‘unknown-error-processor > )
Теперь создаем тестовые подпрограммы, возвращающие разные коды возврата и соответствующий результат:
Работа нашего оператора в данном случае выглядит так:
Здесь в каждое тело состоит только из одного метода. В реальности можно вместо него вставить любую последовательность функций.
Преимущество данного оператора состоит в том, что обработка новых кодов или изменение обработчиков существующих, осуществляется прозрачно путем реализации обработчиков и внесением соответсвующих изменений в mapping, который может находиться в отдельном файле.
Никаких if-ов. Данный подход реализует достаточно гибкий паттерн обработки кодов возврата.
process-exception
По аналогии с предыдущим примером. Есть функции обработки некоторых исключений:
( defn arithmetic-exception-processor [ e ]
( println ( str «Arithmetic exception.» ) ) )
( defn nullpointer-exception-processor [ e ]
( println ( str «Nullpointer exception.» ) ) )
( defn another-exception-processor [ e ]
( println ( str «Other exception.» ) ) )
Map исключений на наименования обрабатывающих их функций:
Создаем тестовые подпрограммы, генерирующие разные исключения:
Работа нашего оператора:
( process-exception exception-mapping
( test-call-ok ) )
«test result»
Замечания в конце описания предыдущего оператора применимы и здесь.
Выводы
Данные операторы очень похожи и в принципе реализуют один и тот же паттерн обработки ответа, но с разной реализацией. Здесь я предпочтение отдаю process-retcode-answer. Хотя в других языках варианты с кодами возврата не всегда выгодны в сравнении с вариантами, использующих исключения (конечно, зависит от условий задачи и самого языка — это уже обсудили).
Вот вариант реализации вышеописанных операторов:
( defmacro process-exception [ mapping & body ]
( let [ catch-items ( map ( fn [ m ]
` ( catch
( eval ( second m ) ) e# ) ) )
( eval mapping ) ) ]
` ( try
( defmacro process-retcode-answer [ mapping & body ]
` ( let [ answer# ( do
@body )
retcode# ( first answer# )
result# ( second answer# )
processor# ( get
mapping ) processor# ) ]
( ( eval processor# ) result# ) ) )
Этот довольно утрированный пример показывает, что базовые элементы языка не сильно важны, как возможность расширять язык новыми конструкциями и также иметь в нем те же функции высокого порядка, лямбды, замыкания. Такой язык позволяет программисту стать художником. Он не пишет паттерны снова и снова. Он просто «создает» язык, в котором может наиболее естественно, наглядно и кратко сформулировать решение поставленной перед ним задачи. Профессия становится не ремеслом, а искусством.
Как использовать коды завершения в Bash-скриптах
Инструменты автоматизации и мониторинга удобны тем, что разработчик может взять готовые скрипты, при необходимости адаптировать и использовать в своём проекте. Можно заметить, что в некоторых скриптах используются коды завершения (exit codes), а в других нет. О коде завершения легко забыть, но это очень полезный инструмент. Особенно важно использовать его в скриптах командной строки.
Что такое коды завершения
В Linux и других Unix-подобных операционных системах программы во время завершения могут передавать значение родительскому процессу. Это значение называется кодом завершения или состоянием завершения. В POSIX по соглашению действует стандарт: программа передаёт 0 при успешном исполнении и 1 или большее число при неудачном исполнении.
Почему это важно? Если смотреть на коды завершения в контексте скриптов для командной строки, ответ очевиден. Любой полезный Bash-скрипт неизбежно будет использоваться в других скриптах или его обернут в однострочник Bash. Это особенно актуально при использовании инструментов автоматизации типа SaltStack или инструментов мониторинга типа Nagios. Эти программы исполняют скрипт и проверяют статус завершения, чтобы определить, было ли исполнение успешным.
Кроме того, даже если вы не определяете коды завершения, они всё равно есть в ваших скриптах. Но без корректного определения кодов выхода можно столкнуться с проблемами: ложными сообщениями об успешном исполнении, которые могут повлиять на работу скрипта.
Что происходит, когда коды завершения не определены
В Linux любой код, запущенный в командной строке, имеет код завершения. Если код завершения не определён, Bash-скрипты используют код выхода последней запущенной команды. Чтобы лучше понять суть, обратите внимание на пример.
Как использовать коды завершения в Bash-скриптах
Проверяем коды завершения
После рефакторинга скрипта получаем такое поведение:
Создаём собственный код завершения
Как использовать коды завершения в командной строке
Скрипт уже умеет сообщать пользователям и программам об успешном или неуспешном выполнении. Теперь его можно использовать с другими инструментами администрирования или однострочниками командной строки.
Скрипт использует коды завершения, чтобы понять, была ли команда успешно выполнена. Если коды завершения используются некорректно, пользователь скрипта может получить неожиданные результаты при неудачном выполнении команды.
Дополнительные коды завершения
Адаптированный перевод статьи Understanding Exit Codes and how to use them in bash scripts by Benjamin Cane. Мнение администрации Хекслета может не совпадать с мнением автора оригинальной публикации.
Коды возврата
В этом разделе описаны коды возврата и сообщения об ошибках средства миграции пользовательского состояния (USMT) 10.0. Также включена таблица с перечислением кодов возврата USMT со связанными шагами по смягчению последствий. Кроме того, в этом разделе данная статья содержит советы, которые помогут вам использовать журналы для определения причин, по которым произошла ошибка.
Понимание требований к запуску USMT поможет свести к минимуму ошибки в миграциях USMT. Дополнительные сведения см. в сведениях о требованиях USMT.
В этой теме
Коды возврата USMT
Если в миграции USMT произошла ошибка, можно использовать коды возврата и более конкретные сведения, предоставляемые в связанных сообщениях об ошибках USMT для устранения неполадок и определения действий по устранению неполадок.
Коды возврата сгруппировали в следующие широкие категории, описывая их область отчетности об ошибках:
Успех или отмена пользователя
Недействительные командные строки
Настройка и инициализация
Ошибки без смертельных исходов
Ошибки со смертельным исходом
В качестве наилучшей практики рекомендуется установить уровень многословия до 5,/v:5 на командных строках ScanState, LoadStateи USMTUtils, чтобы наиболее подробные отчеты были доступны в соответствующих журналах USMT. Вы можете использовать более высокий уровень многословия, если вы хотите, чтобы выход файлов журналов переходить к отладите.
Сообщения об ошибках USMT
Сообщения об ошибках предоставляют более подробные сведения о проблеме миграции, чем связанный код возврата. Например, средство ScanState, LoadStateили USMTUtils может возвращать код «11» (для «USMT_INVALID_PARAMETERS») и связанное сообщение об ошибке, которое читает «/key and/keyfile оба указанных». Сообщение об ошибке отображается в командной подсказке и определяется в файлах журнала ScanState, LoadStateили USMTUtils, чтобы определить, почему был получен код возврата.
Вы можете получить дополнительные сведения о Windows кодов системных ошибок интерфейса программирования приложений (API), введя в строку командной строки net helpmsg и введя номер кода ошибки. Дополнительные сведения о кодах системных ошибок см. в этом веб-сайте Microsoft.
Устранение неполадок кодов возврата и сообщений об ошибках
В следующей таблице перечислены все возвращающие коды по числовому значению, а также связанные сообщения об ошибках и предлагаемые действия по устранению неполадок.