Quais são os valores mínimo e máximo dos códigos de saída no Linux?

40

Quais são os valores mínimo e máximo dos seguintes códigos de saída no Linux:

  1. O código de saída retornado de um executável binário (por exemplo: um programa em C).
  2. O código de saída retornado de um script bash (ao chamar exit).
  3. O código de saída retornado de uma função (ao chamar return). Eu acho que isso é entre 0e 255.
user271801
fonte
Para a parte 3, você quer dizer retornar de uma função shell ? Isso pode depender do shell, mas observo que o manual do Bash diz que "os status de saída caem entre 0 e 255 " e "os status de saída dos buildins e comandos compostos do shell também são limitados a esse intervalo " .return É, obviamente, um shell embutido.
Toby Speight
Relacionado (tem respostas para a maioria das suas perguntas): Código de saída padrão quando o processo é encerrado?
Stéphane Chazelas
@ TobySpeight, essa é uma limitação do bashshell. Alguns outros shells zshpodem retornar qualquer valor assinado de 32 bits como para exit. Alguns gostam rcou espodem retornar dados de qualquer um dos tipos suportados (escalar ou lista). Consulte as perguntas e respostas vinculadas para obter detalhes.
Stéphane Chazelas

Respostas:

74

O número passado para a chamada _exit()/ exit_group()system (às vezes chamado de código de saída para evitar a ambiguidade com o status de saída), que também se refere a uma codificação do código de saída ou número do sinal e informações adicionais, dependendo se o processo foi encerrado ou encerrado normalmente ) é do tipo int, portanto, em sistemas do tipo Unix, como o Linux, geralmente um número inteiro de 32 bits com valores de -2147483648 (-2 31 ) a 2147483647 (2 31 -1).

No entanto, em todos os sistemas, quando o processo pai (ou a subreaper criança ou initse o pai morreu) usa os wait(), waitpid(), wait3(), wait4()chamadas de sistema para recuperá-lo, apenas os 8 bits inferiores de que são disponíveis (valores de 0 a 255 (2 8 - 1)).

Ao usar a waitid()API (ou um manipulador de sinal no SIGCHLD), na maioria dos sistemas (e como o POSIX agora exige mais claramente na edição de 2016 do padrão (consulte a _exit()especificação )), o número completo está disponível (no si_statuscampo da estrutura retornada ) Esse ainda não é o caso do Linux, embora também trunque o número de 8 bits com a waitid()API, mas isso provavelmente mudará no futuro.

Geralmente, você deseja usar apenas os valores 0 (geralmente significando sucesso) a 125 apenas, pois muitos shells usam valores acima de 128 na $?representação do status de saída para codificar o número do sinal de um processo que está sendo morto e 126 e 127 para funções especiais. condições.

Você pode usar 126 a 255 exit()para significar a mesma coisa que eles fazem para os shell $?(como quando um script faz ret=$?; ...; exit "$ret"). Usar valores fora de 0 -> 255 geralmente não é útil. Geralmente, você faria isso apenas se souber que o pai usará a waitid()API em sistemas que não são truncados e se você precisar do intervalo de valores de 32 bits. Observe que, se você fizer um, exit(2048)por exemplo, isso será visto como sucesso pelos pais usando as wait*()APIs tradicionais .

Mais informações em:

Esperamos que as perguntas e respostas respondam à maioria das outras perguntas e esclareçam o significado de status de saída . Vou adicionar mais algumas coisas:

Um processo não pode terminar, a menos que seja morto ou faça as chamadas _exit()/ exit_group()system. Quando você retorna de main()in C, a libc chama essa chamada de sistema com o valor de retorno.

A maioria dos idiomas possui uma exit()função que agrupa a chamada do sistema e o valor que eles assumem, se algum for geralmente passado como é para a chamada do sistema. (observe que geralmente fazem mais coisas como a limpeza feita pela exit()função de C que libera os buffers stdio, executa os atexit()ganchos ...)

Esse é o caso de pelo menos:

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

Ocasionalmente, você vê algumas reclamações quando usa um valor fora de 0 a 255:

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

Algumas conchas reclamam quando você usa um valor negativo:

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

O POSIX deixa o comportamento indefinido se o valor passado para o exitbuilt-in especial estiver fora de 0-> 255.

Algumas conchas mostram comportamentos inesperados se você:

  • bash(e mkshnão pdkshno qual se baseia) se encarrega de truncar o valor para 8 bits:

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

    Portanto, nessas shells, se você quiser sair com um valor fora de 0-255, precisará fazer algo como:

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

    Ou seja, execute outro comando no mesmo processo que pode chamar a chamada do sistema com o valor desejado.

  • como mencionado nas outras perguntas e respostas, ksh93tem o comportamento mais estranho para valores de saída de 257 a 256 + max_signal_number onde, em vez de chamar exit_group(), ele se mata com o sinal correspondente¹.

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

    e trunca o número como bash/ mksh.


É provável que isso mude na próxima versão. Agora que o desenvolvimento ksh93foi retomado como um esforço da comunidade fora da AT&T, esse comportamento, embora encorajado de alguma forma pelo POSIX, está sendo revertido

Stéphane Chazelas
fonte
2
Você sabe se há alguma discussão sobre como implementar o código de saída completo no si_statusLinux?
Ruslan
2
@Ruslan, não mais do que o austingroupbugs.net/view.php?id=594#c1318 (de Eric Blake (RedHat)) no link que eu dei
Stéphane Chazelas
11
"é do tipo int, então um número inteiro de 32 bits". O Linux realmente garante que um int sempre será 32 bits? Mesmo quando rodando em alguns desses minúsculos microcontroladores? Isso me parece realmente estranho. POSIX certamente não.
Voo 24/01
@Voo, esses minúsculos microcontroladores não podem rodar Linux. Enquanto C requer intpelo menos 16 bits, o POSIX exige mais ou menos 32 bits e os ambientes de programação possuem um uint32_t . Não sei se o Linux suporta qualquer ambiente de programação em que ints tenham nada além de 32 bits, nunca encontrei nenhum.
Stéphane Chazelas
11
Em um sistema operacional POSIX compatível, você pode obter o código de saída completo de 32 bits na versão recente do Bourne Shell, consulte: schillix.sourceforge.net/man/man1/bosh.1.html
schily
12

O mínimo é 0, e isso é considerado o valor do sucesso. Todos os outros são fracassos. O máximo 255também é conhecido como -1.

Essas regras se aplicam a scripts e outros executáveis, além de funções de shell.

Valores maiores resultam no módulo 256.

Tomasz
fonte
2
Para ser preciso, em alguns shells tipo Bourne (mas não bashou outros mais usados) o código de saída passado para o exitbuilt-in não é tratado como módulo-256 e, em vez disso, causa um erro. (Por exemplo, o comum exit -1na verdade não é um equivalente portátil exit 255na maioria das conchas). E se exit(-1)o nível C é equivalente a exit(255)um detalhe que certamente funcionará, mas depende do comportamento definido pela implementação (embora isso não seja um problema nos sistemas modernos que você provavelmente utilizará na prática).
Mtraceur
Pelo que sei, apenas o ksh93 limita o exit(1)parâmetro a 8 bits.
schily
6

Isso parece tão simples, mas, oh, os problemas.

A linguagem C (e a seguir à maioria das outras linguagens, direta ou indiretamente) exige que retornar de mainseja equivalente a chamar exitcom o mesmo argumento que o valor de retorno. Este é um número inteiro (o tipo de retorno é muito claramente int), de modo que, em princípio, a gama seria INT_MINde INT_MAX.

No entanto, o POSIX afirma que apenas os 8 bits mais baixos passados exitdevem ser disponibilizados para um processo pai em espera, literalmente como se fosse "status & 0xFF" .
Portanto, na prática, o código de saída é um número inteiro (ainda assinado) do qual apenas os 8 bits mais baixos são definidos.

O mínimo será assim -128 e o máximo 127 . Espere, isso não é verdade. Será de 0 a 255.

Mas, infelizmente, é claro que não pode ser assim tão simples . Na prática, o Linux (ou melhor, o bash) faz diferente . O intervalo válido de códigos de retorno é de 0 a 255 (isto é, não assinado).

Para estar do lado seguro em termos de evitar confusão, provavelmente é uma boa idéia apenas supor que os códigos de retorno não tenham sinal e transmitir tudo do que você voltar waitpara não assinado. Dessa forma, é consistente com o que você vê em uma concha. Como os bits mais altos (incluindo o mais significativo) são limpos, isso nem é "errado" porque, embora tecnicamente assinado, os valores reais sempre são sem sinal (pois o bit de sinal nunca é definido).
Também ajuda a evitar o erro comum de comparar um código de saída -1, o qual, por algum motivo estranho, parece nunca aparecer mesmo quando um programa sai com -1(bem, adivinhe por que!).

Sobre o seu último ponto, retornando de uma função, se essa função acontecer main, veja acima. Caso contrário, depende do tipo de retorno da função, poderia, em princípio, ser qualquer coisa (inclusive void).

Damon
fonte
Você estava certo antes de 1989, quando waitid()foi introduzido.
schily
@schily: Não sabe o que você quer dizer? waitid()faz exatamente a mesma coisa, um pouco diferente. Ele espera por um ID específico ou por qualquer encadeamento e grava os resultados na siginfo_testrutura apontada para onde si_statusestá int(assim ... assinado , da mesma forma). Ainda assim, exit()só passa os 8 bits mais baixos, então ... absolutamente a mesma coisa sob o capô.
Damon
exit()passa todos os 32 bits do parâmetro para o kernel e waitid()retorna todos os 32 bits do código de saída. Talvez você tenha verificado no Linux, onde ninguém se importa em corrigir bugs. Se você não acredita em mim, verificá-lo em um complient OS POSIX ...
Schily
@ Schily: Se isso é verdade (acho que não, mas mesmo assim), o Linux está quebrado . Leia a especificação POSIX vinculada a resposta exit, em particular a segunda linha em "Descrição", que afirma: "embora apenas os 8 bits menos significativos (ou seja, status & 0377) estejam disponíveis para um processo pai em espera " . É assim que uma implementação em conformidade funciona - 8 bits mais baixos, não 32. Você tem uma referência para 32 bits sendo transmitidos?
Damon
Eu pensei ter mencionado que o Linux está quebrado. Pior ainda: o pessoal do kernel do Linux se recusa a corrigir bugs. Se você ler o padrão POSIX, verá que a versão de 1995 (SUSv1) explica corretamente o recurso originalmente introduzido pelo SVr4 em 1989 e que as versões recentes (por exemplo, SUSv7tc2) do padrão explicam explicitamente isso waitid()e a siginfo_testrutura passada ao SIGCHLDretorno do manipulador todos os 32 bits do exit()parâmetro
schily 28/10
2
  1. O código de saída retornado de um executável binário (por exemplo: um programa em C).
  2. O código de saída retornado de um script bash (ao chamar exit).

Os códigos de saída de qualquer processo - seja um executável binário, um script de shell ou qualquer outra coisa - variam de 0 a 255. É possível passar um valor maior para exit(), mas apenas os 8 bits inferiores do status são disponibilizados para outros processos através wait().

  1. O código de saída retornado de uma função (ao chamar return). Eu acho que isso é entre 0 e 255.

A função CA pode ser declarada como retornando quase qualquer tipo. Os limites de seu valor de retorno são determinados inteiramente por esse tipo: por exemplo, -128 a 127 para uma função retornando signed char, ou 0 a 4,2 bilhões para uma função retornando unsigned int, ou qualquer número de ponto flutuante até e inclusive infpara uma função retornando double. E isso não está contando os tipos não numéricos, como void *ou um struct...

crepúsculo
fonte