Jakie są minimalne i maksymalne wartości kodów wyjścia w systemie Linux?

40

Jakie są minimalne i maksymalne wartości następujących kodów wyjścia w systemie Linux:

  1. Kod wyjścia zwrócony z binarnego pliku wykonywalnego (na przykład: program w języku C).
  2. Kod wyjścia zwrócony ze skryptu bash (podczas wywoływania exit).
  3. Kod wyjścia zwrócony z funkcji (podczas wywoływania return). Myślę, że to jest pomiędzy 0i 255.
użytkownik 271801
źródło
Czy w części 3 masz na myśli powrót z funkcji powłoki ? Może to zależeć od powłoki, ale zauważam, że instrukcja Basha mówi: „ Statusy wyjścia mieszczą się w przedziale od 0 do 255 ”, a „ Statusy wyjść z wbudowanych powłok i poleceń złożonych również są ograniczone do tego zakresu.returnTo oczywiście wbudowana powłoka.
Toby Speight
Powiązane (ma odpowiedzi na większość pytań): Domyślny kod wyjścia po zakończeniu procesu?
Stéphane Chazelas
@TobySpeight, to ograniczenie bashpowłoki. Niektóre inne powłoki zshmogą zwracać dowolną 32-bitową wartość ze znakiem, jak dla exit. Niektórzy lubią rclub esmogą zwracać dane dowolnego obsługiwanego typu (skalar lub lista). Szczegółowe informacje można znaleźć w powiązanych pytaniach i odpowiedziach.
Stéphane Chazelas

Odpowiedzi:

74

Numer przekazywany do wywołania systemowego _exit()/ exit_group()(czasami nazywany kodem wyjścia, aby uniknąć niejednoznaczności ze statusem wyjścia, który odnosi się również do kodowania kodu wyjścia lub numeru sygnału i dodatkowych informacji w zależności od tego, czy proces został zabity, czy zakończony normalnie ) jest typu int, więc w systemach uniksopodobnych, takich jak Linux, zwykle jest to 32-bitowa liczba całkowita o wartościach od -2147483648 (-2 31 ) do 2147483647 (2 31 -1).

Jednak we wszystkich systemach, kiedy proces nadrzędny (lub subreaper dziecko lub initjeśli rodzic zmarł) używa wait(), waitpid(), wait3(), wait4()wywołania systemowe, aby je odzyskać, tylko dolne 8 bitów to są dostępne (wartości od 0 do 255 (2 8 - 1)).

Podczas korzystania z waitid()interfejsu API (lub procedury obsługi sygnału w SIGCHLD) w większości systemów (a ponieważ POSIX bardziej wyraźnie wymaga tego w wersji standardowej 2016 (patrz _exit()specyfikacja )) dostępna jest pełna liczba (w si_statuspolu zwracanej struktury ). Nie jest tak jednak w przypadku Linuksa, który również zmniejsza liczbę waitid()interfejsów API do 8 bitów , choć może się to zmienić w przyszłości.

Ogólnie rzecz biorąc, chciałbyś używać tylko wartości 0 (ogólnie oznacza sukces) tylko do 125, ponieważ wiele powłok używa wartości powyżej 128 w ich $?reprezentacji statusu wyjścia do zakodowania numeru sygnału zabijanego procesu oraz 126 i 127 dla specjalnych warunki.

Możesz użyć 126 do 255, exit()aby oznaczać to samo, co w przypadku powłoki $?(tak jak w przypadku skryptu ret=$?; ...; exit "$ret"). Używanie wartości spoza 0 -> 255 na ogół nie jest przydatne. Zasadniczo zrobiłbyś to tylko wtedy, gdy wiesz, że rodzic użyje waitid()interfejsu API w systemach, które nie są obcinane, a zdarza się, że potrzebujesz 32-bitowego zakresu wartości. Pamiętaj, że jeśli wykonasz exit(2048)np., Rodzice będą postrzegać to jako sukces przy użyciu tradycyjnych wait*()interfejsów API.

Więcej informacji na:

Mam nadzieję, że pytania i odpowiedzi powinny odpowiedzieć na większość pozostałych pytań i wyjaśnić, co oznacza status wyjścia . Dodam jeszcze kilka rzeczy:

Proces nie może zostać zakończony, dopóki nie zostanie zabity lub nie wywoła wywołań _exit()/ exit_group()system. Po powrocie z main()w C, zaproszeń libc tego wywołania systemowego z wartości zwracanej.

Większość języków ma exit()funkcję, która otacza to wywołanie systemowe, oraz wartość, którą przyjmują, jeśli są one generalnie przekazywane tak samo jak wywołanie systemowe. (zauważ, że generalnie robią więcej rzeczy, takich jak czyszczenie wykonane przez exit()funkcję C, która opróżnia bufory stdio, uruchamia atexit()haki ...)

Tak jest w przypadku co najmniej:

$ 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)

Czasami widzisz osoby, które narzekają, gdy używasz wartości spoza 0-255:

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

Niektóre pociski skarżą się, gdy użyjesz wartości ujemnej:

$ 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 pozostawia zachowanie niezdefiniowane, jeśli wartość przekazana do exitspecjalnego wbudowanego kodu jest poza 0-> 255.

Niektóre powłoki wykazują pewne nieoczekiwane zachowania, jeśli:

  • bash( mkshale nie pdkshna którym się opiera) bierze na siebie obcinanie wartości do 8 bitów:

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

    Więc w tych powłokach, jeśli chcesz wyjść z wartością spoza 0-255, musisz zrobić coś takiego:

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

    To jest wykonanie innej komendy w tym samym procesie, która może wywołać wywołanie systemowe o żądanej wartości.

  • jak wspomniano w innych ksh93pytaniach i odpowiedziach, ma najdziwniejsze zachowanie dla wartości wyjściowych od 257 do 256 + max_signal_number gdzie zamiast wywoływać exit_group(), zabija się odpowiednim sygnałem¹.

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

    i inaczej obcina liczbę, np . bash/ mksh.


¹ Prawdopodobnie zmieni się to w następnej wersji. Teraz, gdy rozwój ksh93został przejęty jako wysiłek społeczności poza AT&T, zachowanie to, choć w jakiś sposób wspierane przez POSIX, zostaje cofnięte

Stéphane Chazelas
źródło
2
Czy wiesz, czy jest jakaś dyskusja na temat implementacji pełnego kodu wyjścia w si_statussystemie Linux?
Ruslan
2
@Ruslan, nie więcej niż austingroupbugs.net/view.php?id=594#c1318 (od Erica Blake'a (RedHat)) pod linkiem, który podałem
Stéphane Chazelas
1
„jest typu int, więc 32-bitowa liczba całkowita”. Linux naprawdę gwarantuje, że int zawsze będzie 32-bitowy? Nawet jeśli działa na niektórych z tych małych mikrokontrolerów? To wydaje mi się naprawdę dziwne. POSIX na pewno nie.
Voo
@ Voo, te małe mikrokontrolery nie mogą uruchomić Linuksa. Podczas gdy C wymaga intco najmniej 16 bitów, POSIX mniej więcej wymaga co najmniej 32 bitów, a środowiska programowania muszą mieć uint32_t . Nie wiem, czy Linux obsługuje jakiekolwiek środowisko programistyczne, w którym ints jest czymś innym niż 32 bity, nigdy nie spotkałem żadnego.
Stéphane Chazelas
1
W systemie operacyjnym zgodnym z POSIX możesz uzyskać pełny 32-bitowy kod wyjścia w najnowszej wersji powłoki Bourne Shell, patrz: schillix.sourceforge.net/man/man1/bosh.1.html
schily
12

Minimum to 0i jest to uważane za wartość sukcesu. Wszystkie pozostałe są porażką. Maksimum jest 255także znane jako -1.

Reguły te dotyczą zarówno skryptów i innych plików wykonywalnych, jak i funkcji powłoki.

Większe wartości wynikają z modulo 256.

Tomasz
źródło
2
Mówiąc ściślej, w niektórych powłokach podobnych do Bourne'a (ale nie bashlub innych najczęściej używanych) kod wyjścia przekazywany do exitwbudowanego nie jest traktowany jako modulo-256, a zamiast tego powoduje błąd. (Na przykład, wspólne nie exit -1jest w rzeczywistości przenośnym odpowiednikiem exit 255większości powłok). A to, czy exit(-1)na poziomie C jest równoważne, exit(255)jest szczegółem, który de facto na pewno zadziała, ale zależy od zachowania określonego dla implementacji (chociaż nie jest to problem w nowoczesnych systemach, których prawdopodobnie będziesz używać w praktyce).
mtraceur
Z tego co wiem, tylko ksh93 ogranicza exit(1)parametr do 8 bitów.
schily
6

To wygląda tak prosto, ale o nieszczęście.

Język C (i podążanie za tym większością innych języków bezpośrednio lub pośrednio) wymaga, aby powrót z mainbył równoważny wywołaniu exitz tym samym argumentem co wartość zwracana. Jest to liczba całkowita (typ zwracany jest bardzo wyraźnie int), więc w zasadzie zakres byłby INT_MINdo INT_MAX.

Jednak POSIX stwierdza, że tylko 8 najniższych przekazanych bitów exitzostanie udostępnionych oczekującemu procesowi nadrzędnemu, dosłownie tak, jakby to był „status & 0xFF” .
W praktyce więc kodem wyjścia jest (wciąż podpisana) liczba całkowita, w której ustawionych jest tylko 8 najniższych bitów.

Minimalna będzie więc -128, a maksymalna 127 . Poczekaj, to nieprawda. Będzie to od 0 do 255.

Ale niestety, oczywiście nie może to być takie proste . W praktyce Linux (a raczej bash) robi to inaczej . Prawidłowy zakres kodów powrotu wynosi od 0 do 255 (tj. Bez znaku).

Aby być bezpiecznym, jeśli chodzi o unikanie pomyłek, prawdopodobnie dobrze jest po prostu założyć, że kody zwrotne są niepodpisane i przesłać wszystko, co wrócisz waitdo niepodpisanego. W ten sposób jest to spójne z tym, co widzisz w powłoce. Ponieważ najwyższe bity (w tym najważniejszy) są usuwane, nie jest to nawet „złe”, ponieważ chociaż technicznie podpisane, rzeczywiste wartości są zawsze niepodpisane (ponieważ bit znaku nigdy nie jest ustawiony).
Pomaga także uniknąć typowego błędu porównywania kodu wyjściowego -1, z którego z dziwnych powodów wydaje się, że nie pojawia się nawet po wyjściu z programu -1(no cóż, dlaczego!).

O twoim ostatnim punkcie, powracaniu z funkcji, jeśli ta funkcja się zdarzy main, to patrz wyżej. W przeciwnym razie zależy to od typu zwracanego przez funkcję, może to być w zasadzie cokolwiek (w tym void).

Damon
źródło
Miałeś rację przed rokiem 1989, kiedy waitid()został wprowadzony.
schily
@schily: Nie wiesz, co masz na myśli? waitid()robi to samo, nieco inaczej. Oczekuje na konkretny identyfikator lub dowolny wątek i zapisuje wyniki do wskazanej siginfo_tstruktury, w której si_statusjest int(tak ... podpisany , po prostu taki sam). Mimo exit()to przechodzi tylko najniższe 8 bitów, więc ... absolutnie to samo pod maską.
Damon
exit()przekazuje wszystkie 32 bity parametru do jądra i waitid()zwraca wszystkie 32 bity z kodu wyjścia. Może sprawdziłeś na Linuxie, gdzie nikt nie chce naprawiać błędów. Jeśli mi nie wierzysz, sprawdź system operacyjny zgodny z POSIX ...
schily
@schily: Jeśli to prawda (nie sądzę, ale tak czy inaczej), to Linux jest zepsuty . Przeczytaj specyfikację POSIX połączoną z odpowiedzią exit, w szczególności drugą linię pod „Opis”, która stanowi: „chociaż tylko 8 najmniej znaczących bitów (to znaczy status i 0377) będzie dostępnych dla oczekującego procesu nadrzędnego „ . Tak działa implementacja zgodna - najniższe 8 bitów, a nie 32. Czy masz odniesienie do przekazywania 32 bitów?
Damon
Myślałem, że wspomniałem, że Linux jest zepsuty. Co gorsza: ludzie jądra Linuksa odmawiają naprawy błędów. Jeśli przeczytasz standard POSIX, przekonasz się, że wersja z 1995 roku (SUSv1) poprawnie wyjaśnia funkcję pierwotnie wprowadzoną przez SVr4 w 1989 roku, a ostatnie wersje (np. SUSv7tc2) standardu nawet jawnie to wyjaśniają, waitid()a siginfo_tstruktura przekazana do programu SIGCHLDobsługi wszystkie 32 bity od exit()parametru.
schily
2
  1. Kod wyjścia zwrócony z binarnego pliku wykonywalnego (na przykład: program w języku C).
  2. Kod wyjścia zwrócony ze skryptu bash (podczas wywoływania exit).

Kody wyjścia z dowolnego procesu - czy to binarnego pliku wykonywalnego, skryptu powłoki, czy cokolwiek innego - mieszczą się w zakresie od 0 do 255. Można przekazać większą wartość exit(), ale tylko 8 niższych bitów statusu jest udostępnianych inne procesy poprzez wait().

  1. Kod wyjścia zwrócony z funkcji (podczas wywoływania return). Myślę, że jest to między 0 a 255.

Funkcja AC może być zadeklarowana jako zwracająca prawie dowolny typ. Granice jego wartości zwracanej są całkowicie określane przez ten typ: na przykład od -128 do 127 dla zwracanej funkcji signed charlub od 0 do 4,2 miliarda dla zwracanej funkcji unsigned intlub dowolna liczba zmiennoprzecinkowa do infzwracanej funkcji włącznie double. I że nie liczy typy non-numeryczne, jak void *albo struct...

duskwuff
źródło