Каковы минимальные и максимальные значения кодов выхода в Linux?

40

Каковы минимальные и максимальные значения следующих кодов выхода в Linux:

  1. Код завершения, возвращаемый двоичным исполняемым файлом (например, программой на Си).
  2. Код выхода, возвращаемый скриптом bash (при вызове exit).
  3. Код выхода, возвращаемый функцией (при вызове return). Я думаю, что это между 0и 255.
user271801
источник
Для части 3 вы имеете в виду возврат из функции оболочки ? Это может зависеть от оболочки, но я отмечаю , что руководство Bash говорит « Выход статусы падают между 0 и 255 » и « Выходом статусами из оболочки встроенных команд и составных команд также ограничены этот диапазон. » return, Конечно же , встроенная команда оболочки.
Тоби Спейт
Связанный (содержит ответы на большинство ваших вопросов): Код завершения по умолчанию, когда процесс завершается?
Стефан Шазелас
@TobySpeight, это ограничение bashоболочки. Некоторые другие оболочки вроде zshмогут возвращать любое 32-битное значение со знаком, например, для exit. Некоторые любят rcили esмогут возвращать данные любого из поддерживаемых ими типов (скаляр или список). Смотрите связанные вопросы и ответы для деталей.
Стефан Шазелас

Ответы:

74

Номер передается _exit()/ exit_group()системного вызова (иногда называемый как код завершения , чтобы избежать неоднозначности с статусом выхода , который также со ссылкой на кодирование либо кода выхода или номер сигнала и дополнительной информации в зависимости от того, был ли процесс убит или вышли нормально ) имеет тип int, поэтому в Unix-подобных системах, таких как Linux, обычно это 32-битное целое число со значениями от -2147483648 (-2 31 ) до 2147483647 (2 31 -1).

Тем не менее, на все системы, когда (ребенок subreaper или или родительский процесс , initесли родитель умер) использует wait(), waitpid(), wait3(), wait4()системные вызовы , чтобы получить его, только младшие 8 бит него доступно (значение от 0 до 255 (2 8 - 1)).

При использовании waitid()API (или обработчика сигнала в SIGCHLD) в большинстве систем (и, как теперь более четко требуется в POSIX в редакции стандарта 2016 года (см. _exit()Спецификацию )), доступен полный номер (в si_statusполе возвращаемой структуры ). Это не относится к Linux, хотя он также усекает число до 8 бит с помощью waitid()API, хотя это может измениться в будущем.

Как правило, вы хотите использовать только значения от 0 (обычно означающие успех) до 125, так как многие оболочки используют значения выше 128 в своем $?представлении состояния выхода для кодирования номера сигнала уничтожаемого процесса и 126 и 127 для специальных условия.

Возможно, вы захотите использовать 126–255 exit()для обозначения того же, что и для оболочки $?(например, для сценария ret=$?; ...; exit "$ret"). Использование значений вне 0 -> 255 обычно бесполезно. Как правило, вы делаете это только в том случае, если знаете, что родитель будет использовать waitid()API в системах, которые не усекаются, и вам необходим 32-битный диапазон значений. Обратите внимание, что если вы сделаете, exit(2048)например, это будет восприниматься родителями как успешное использование традиционных wait*()API.

Больше информации на:

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

Процесс не может быть прекращено , если он не убил или вызывает _exit()/ exit_group()системные вызовы. Когда вы возвращаетесь из main()in C, libc вызывает этот системный вызов с возвращаемым значением.

Большинство языков имеют exit()функцию, которая переносит этот системный вызов, и значение, которое они принимают, если оно вообще передается системному вызову. (обратите внимание, что они обычно делают больше вещей, таких как очистка, выполняемая exit()функцией C, которая очищает буферы stdio, запускает atexit()ловушки ...)

Это случай по крайней мере:

$ strace -e exit_group awk 'BEGIN{exit(1234)}'
exit_group(1234)                        = ?
$ strace -e exit_group mawk 'BEGIN{exit(1234)}'
exit_group(1234)                        = ?
$ strace -e exit_group busybox awk 'BEGIN{exit(1234)}'
exit_group(1234)                        = ?
$ echo | strace -e exit_group sed 'Q1234'
exit_group(1234)                        = ?
$ strace -e exit_group perl -e 'exit(1234)'
exit_group(1234)                        = ?
$ strace -e exit_group python -c 'exit(1234)'
exit_group(1234)                        = ?
$ strace -e exit_group expect -c 'exit 1234'
exit_group(1234)                        = ?
$ strace -e exit_group php -r 'exit(1234);'
exit_group(1234)                        = ?
$ strace -e exit_group zsh -c 'exit 1234'
exit_group(1234)

Иногда вы видите, что жалуются, когда вы используете значение за пределами 0-255:

$ echo 'm4exit(1234)' | strace -e exit_group m4
m4:stdin:1: exit status out of range: `1234'
exit_group(1)                           = ?

Некоторые оболочки жалуются, когда вы используете отрицательное значение:

$ strace -e exit_group dash -c 'exit -1234'
dash: 1: exit: Illegal number: -1234
exit_group(2)                           = ?
$ strace -e exit_group yash -c 'exit -- -1234'
exit: `-1234' is not a valid integer
exit_group(2)                           = ?

POSIX оставляет поведение неопределенным, если значение, переданное exitспециальной встроенной функции, находится вне 0-> 255.

Некоторые оболочки показывают неожиданное поведение, если вы делаете:

  • bashmkshне pdkshна чем он основан) берет на себя усечение значения до 8 бит:

    $ strace -e exit_group bash -c 'exit 1234'
    exit_group(210)                         = ?
    

    Так что в этих оболочках, если вы хотите выйти со значением вне 0-255, вы должны сделать что-то вроде:

    exec zsh -c 'exit -- -12345'
    exec perl -e 'exit(-12345)'
    

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

  • как упоминалось в этом другом Q & A, ksh93имеет самое странное поведение для значений выхода от 257 до 256 + max_signal_number, где вместо вызова exit_group()он убивает себя соответствующим сигналом¹.

    $ ksh -c 'exit "$((256 + $(kill -l STOP)))"'
    zsh: suspended (signal)  ksh -c 'exit "$((256 + $(kill -l STOP)))"'
    

    и иначе усекает число как bash/ mksh.


Likely Это, вероятно, изменится в следующей версии. Теперь, когда разработка ksh93была предпринята как усилие сообщества за пределами AT & T, это поведение, хотя и поощряемое каким-либо образом POSIX, возвращается

Стефан Шазелас
источник
2
Знаете ли вы, есть ли обсуждение реализации полного кода выхода si_statusдля Linux?
Руслан
2
@Ruslan, не больше, чем austingroupbugs.net/view.php?id=594#c1318 (от Эрика Блейка (RedHat)) по ссылке, которую я дал
Стефан Шазелас
1
msgstr "имеет тип int, поэтому 32-битное целое число". Linux действительно гарантирует, что int всегда будет 32-битным? Даже при работе на некоторых из этих крошечных микроконтроллеров? Это кажется мне странным. POSIX, конечно, нет.
Во
@ Voo, эти крошечные микроконтроллеры не могут работать под Linux. Хотя для C требуется intне менее 16 бит, POSIX более или менее требует, чтобы он был не менее 32 бит, а среда программирования должна иметь uint32_t . Я не знаю, поддерживает ли Linux какую-либо среду программирования, где целые числа - это не 32 биты, я никогда не сталкивался с такой.
Стефан Шазелас
1
На POSIX-совместимой ОС вы можете получить полный 32-битный код завершения в последней версии Bourne Shell, см .: schillix.sourceforge.net/man/man1/bosh.1.html
schily
12

Минимум есть 0, и это считается значением успеха. Все остальные неудачи. Максимум 255также известен как -1.

Эти правила применяются как для скриптов, так и для других исполняемых файлов, а также для функций оболочки.

Большие значения приводят к модулю 256.

Tomasz
источник
2
Если быть точным, то в некоторых оболочках типа Bourne (но не в bashдругих или наиболее часто используемых) код завершения, передаваемый во exitвстроенный модуль, не обрабатывается по модулю 256, а вместо этого вызывает ошибку. (Например, общее exit -1на самом деле не является переносимым эквивалентом exit 255в большинстве оболочек). И то, exit(-1)эквивалентен ли уровень C, является exit(255)деталью, которая де-факто наверняка будет работать, но зависит от поведения, определенного реализацией (хотя это не проблема для современных систем, которые вы, вероятно, будете использовать на практике).
mtraceur
Из того, что я знаю, только ksh93 ограничивает exit(1)параметр до 8 бит.
Шили
6

Это выглядит так просто, но о горе.

Язык C (и, следовательно, большинство других языков прямо или косвенно) требует, чтобы возвращение из from mainбыло эквивалентно вызову exitс тем же аргументом, что и возвращаемое значение. Это целое число (тип возвращаемого очень четко int), так что в принципе диапазон будет INT_MINв INT_MAX.

Тем не менее, POSIX заявляет, что только самые младшие 8 передаваемых битов exitдолжны быть доступны ожидающему родительскому процессу, буквально, как если бы это было «status & 0xFF» .
Таким образом, на практике код выхода - это (все еще подписанное) целое число, из которых установлены только самые младшие 8 битов.

Минимум при этом будет -128, а максимум 127 . Погоди, это не правда. Это будет от 0 до 255.

Но, увы, конечно, не все так просто . На практике Linux (точнее, bash) делает это по-другому . Допустимый диапазон кодов возврата - от 0 до 255 (т. Е. Без знака).

Чтобы быть в безопасности во избежание путаницы, вероятно, будет хорошей идеей просто предположить, что коды возврата не подписаны, и привести все, что вы получили, waitк беззнаковым. Таким образом, это согласуется с тем, что вы видите в оболочке. Поскольку самые верхние биты (включая самый старший) очищаются, это даже не «неправильно», потому что, хотя технически они подписаны, фактические значения всегда являются беззнаковыми (поскольку бит знака никогда не устанавливается).
Это также помогает избежать распространенной ошибки сравнения кода выхода -1, с которой по какой-то странной причине, кажется, никогда не появляется, даже когда программа завершает работу -1(ну, угадайте почему!).

О вашем последнем пункте, возвращающемся из функции, если эта функция оказывается main, то смотрите выше. В противном случае это зависит от типа возвращаемого значения функции, в принципе это может быть что угодно (в том числе void).

Damon
источник
Вы были правы до 1989 года, когда вас waitid()представили.
Шили
@schily: Не уверен, что вы имеете в виду? waitid()делает то же самое, немного по-другому. Он ожидает определенного идентификатора или какого-либо потока и записывает результаты в указанную siginfo_tструктуру, где si_statusесть int(так что ... со знаком , точно так же). Тем не менее, exit()только проходит самые нижние 8 бит, так что ... абсолютно то же самое под капотом.
Деймон
exit()передает все 32 бита параметра в ядро ​​и waitid()возвращает все 32 бита из кода выхода. Может быть, вы проверили на Linux, где никто не хочет исправлять ошибки. Если вы мне не верите, проверьте это на POSIX-совместимой ОС ...
schily
@schily: Если это правда (я не думаю , что это, но в любом случае), то Linux будет нарушена . Пожалуйста, прочитайте спецификацию POSIX, связанную с ответом exit, в частности, вторую строку под заголовком «Описание», в которой говорится: «хотя только младшие 8 битов (то есть статус и 0377) должны быть доступны ожидающему родительскому процессу " . Вот как работает соответствующая реализация - 8 младших битов, а не 32. Есть ли у вас ссылка на 32 передаваемых бита?
Деймон
Я думал, что упомянул, что Linux сломан. Еще хуже: люди из ядра Linux отказываются исправлять ошибки. Если вы прочитаете стандарт POSIX, вы обнаружите, что версия 1995 года (SUSv1) правильно объясняет функцию, первоначально введенную SVr4 в 1989 году, и что последние версии (например, SUSv7tc2) стандарта даже явно объясняют это, waitid()и siginfo_tструктура, передаваемая SIGCHLDобработчику, возвращается все 32 бита от exit()параметра.
Шили
2
  1. Код завершения, возвращаемый двоичным исполняемым файлом (например, программой на Си).
  2. Код выхода, возвращаемый скриптом bash (при вызове exit).

Коды выхода из любого процесса - будь то двоичный исполняемый файл, сценарий оболочки или что-то еще - варьируются от 0 до 255. Можно передать большее значение exit(), но только 8 младших битов состояния доступны для другие процессы через wait().

  1. Код выхода, возвращаемый функцией (при вызове return). Я думаю, что это между 0 и 255.

Функция AC может быть объявлена ​​как возвращающая почти любой тип. Пределы его возвращаемого значения полностью определяются этим типом: например, от -128 до 127 для возвращаемой функции signed char, от 0 до 4,2 миллиарда для возвращаемой функции unsigned intили любое число с плавающей запятой вплоть до и включая infвозвращаемую функцию double. И это не считая нечисловых типов, таких как void *или struct...

duskwuff
источник