Які мінімальні та максимальні значення кодів виходу в Linux?


40

Які мінімальні та максимальні значення для наступних кодів виходу в Linux:

  1. Код виходу повернувся з двійкового виконуваного файлу (наприклад, програма C).
  2. Код виходу повернувся із сценарію bash (під час дзвінка exit).
  3. Код виходу повернувся з функції (під час дзвінка return). Я думаю, що це між 0і 255.

Чи маєте на увазі частину 3 повернення з функції оболонки ? Це може залежати від оболонки, але зауважу, що в посібнику Баша сказано, що " Вихідні статуси падають між 0 і 255 ", а " Вихідні статуси з вбудованих оболонок і складені команди також обмежені цим діапазоном " return. Звичайно, це вбудована оболонка.
Toby Speight

Пов'язане (має відповіді на більшість питань): Код виходу за замовчуванням, коли процес закінчується?
Стефан Шазелас

@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()в 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.

Деякі снаряди демонструють деякі несподівані поведінки, якщо ви робите:

  • bash( mkshа не 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)'
    

    Це виконати іншу команду в тому ж процесі, яка може викликати системний виклик із потрібним значенням.

  • як згадувалося в тому іншому запитанні та відповіді, 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.


Though Хоча це зміниться в наступній версії. Тепер, коли розвиток ksh93було взято назустріч зусиллям спільноти поза AT&T, така поведінка, хоча POSIX якось заохочується, повертається.


2
Чи знаєте ви, чи є дискусія щодо впровадження повного виходу коду si_statusдля Linux?
Руслан

2
@Ruslan, не більше ніж austingroupbugs.net/view.php?id=594#c1318 (від Еріка Блейка (RedHat)) за посиланням, яке я дав
Стефан Шазелас

1
"являє собою тип int, тобто 32-бітове ціле число". Linux справді гарантує, що int завжди буде 32-бітним? Навіть коли ви працюєте на деяких із цих крихітних мікроконтролерів? Це вражає мене, як насправді дивним. POSIX звичайно ні.
Voo

@Voo, ці крихітні мікроконтролери не можуть запустити Linux. У той час як C вимагає intпринаймні 16 біт, POSIX більше або менше вимагає, щоб воно було принаймні 32 бітами, а середовища програмування мати uint32_t . Я не знаю, чи підтримує Linux будь-яке середовище програмування, де інти - це щось, крім 32 біт, я ніколи не стикався ні з ким.
Стефан Шазелас

1
На ОС, сумісною з POSIX, ви можете отримати повний 32-бітний код виходу в останній версії оболонки Bourne, див.: Schillix.sourceforge.net/man/man1/bosh.1.html
schily

12

Мінімум є 0, і це вважається величиною успіху. Усі інші - це невдача. Максимум 255також відомий як -1.

Ці правила стосуються як сценаріїв, так і інших виконуваних файлів, а також функцій оболонки.

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


2
Якщо бути точним, в деяких оболонках Борна (але не, bashабо в інших найбільш часто використовуваних) код виходу, переданий exitвбудованому, не трактується як модуль-256, а натомість викликає помилку. (Наприклад, звичайний exit -1насправді не є портативним еквівалентом exit 255у більшості оболонок). І exit(-1)на рівні C еквівалентна exit(255)деталізація, яка фактично не працює, але покладається на поведінку, визначену впровадженням (хоча це не проблема в сучасних системах, які ви, ймовірно, використовуєте на практиці).
mtraceur

З того, що мені відомо, лише ksh93 обмежує exit(1)параметр на 8 біт.
schily

6

Це виглядає так просто, але про горе.

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

Однак POSIX стверджує, що лише найменші 8 біт, переданих до цього, exitповинні бути доступними для батьків, які очікують, буквально так, як якщо б це було "статус і 0xFF" .
Тож на практиці вихідний код - це (все ще підписане) ціле число, з якого встановлено лише найменші 8 біт.

Мінімальний при цьому буде -128, а максимальний 127 . Тримайся, це неправда. Це буде від 0 до 255.

Але на жаль, це, звичайно, не може бути таким простим . На практиці Linux (а точніше bash) робить це інакше . Дійсний діапазон повернених кодів становить від 0 до 255 (тобто без підпису).

Щоб бути в безпеці з точки зору уникнення плутанини, напевно, гарна ідея просто припустити, що коди повернення не підписані, і передати все, що ви повернетеся з waitнепідписаного. Таким чином, це відповідає тому, що ви бачите в оболонці. Оскільки очищаються найвищі біти (включаючи найзначніший), це навіть не "неправильно", оскільки, хоча технічно підписані, фактичні значення завжди не підписані (оскільки біт знаків ніколи не встановлюється).
Це також допомагає уникнути поширеної помилки порівнювання коду виходу -1, яка з якихось дивних причин ніколи не з’являється навіть тоді, коли програма закінчується -1(ну, пригадайте, чому!).

Про ваш останній пункт, що повертається з функції, якщо ця функція трапляється main, то дивіться вище. В іншому випадку це залежить від типу повернення функції, в принципі це може бути будь-що (в тому числі void).


Ви мали рацію до 1989 року, коли waitid()представили.
schily

@schily: Не впевнений, що ти маєш на увазі? waitid()робить те саме, трохи інакше. Він чекає певного ідентифікатора або будь-якого потоку, і він записує результати в структуру, що вказує, siginfo_tде si_statusє int(так ... підписано , саме те саме) Все-таки exit()проходить лише найнижчі 8 біт, так що ... абсолютно те саме під капотом.
Деймон

exit()передає всі 32 біти параметра в ядро ​​і waitid()повертає всі 32 біти з коду виходу. Можливо, ви перевірили Linux, де ніхто не переймається виправленням помилок. Якщо ви мені не вірите, перевірте це на
компліментальній

@schily: Якщо це правда (я не думаю, що це так, але все одно), тоді Linux зламається . Будь ласка, прочитайте специфікацію POSIX з прив’язкою до відповіді exit, зокрема другий рядок у розділі "Опис", де зазначено: "хоча лише найменш значущі 8 біт (тобто статус та 0377) будуть доступними для батьків, які очікують " . Ось так працює відповідна реалізація - найнижчі 8 біт, а не 32. Чи є у вас посилання на 32 біти, що передаються?
Деймон

Я думав, я згадав, що Linux зламаний. Ще гірше: люди з ядром Linux відмовляються виправляти помилки. Якщо ви прочитаєте стандарт POSIX, то виявите, що версія 1995 (SUSv1) коректно пояснює функцію, спочатку введену SVr4 в 1989 році, і що останні версії (наприклад, SUSv7tc2) стандарту навіть явно пояснюють це waitid()і siginfo_tструктуру, передану обробці SIGCHLDповернення всі 32 біти від exit()параметра.
schily

2
  1. Код виходу повернувся з двійкового виконуваного файлу (наприклад, програма C).
  2. Код виходу повернувся із сценарію bash (при виклику виходу).

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

  1. Код виходу повертається з функції (при виклику return). Я думаю, що це між 0 і 255.

Функція змінного струму може бути оголошена як повертається практично будь-якого типу. Межі його значення, що повертаються, повністю визначаються цим типом: наприклад, від -128 до 127 для функції, що повертається signed char, або від 0 до 4,2 мільярда для функції, що повертається unsigned int, або будь-яке число з плаваючою комою до і включаючи infфункцію, що повертається double. І це не враховує нечислові типи, наприклад, void *чи struct...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.