it-swarm.com.de

Standard-Exit-Code beim Beenden des Prozesses?

Wenn ein Prozess mit einem handhabbaren Signal wie SIGINT oder SIGTERM beendet wird, das Signal jedoch nicht verarbeitet wird, wie lautet der Exit-Code des Prozesses?

Was ist mit nicht handhabbaren Signalen wie SIGKILL?

Nach allem, was ich sagen kann, führt das Beenden eines Prozesses mit SIGINT wahrscheinlich zum Exit-Code 130, aber würde das je nach Kernel- oder Shell-Implementierung variieren?

$ cat myScript
#!/bin/bash
sleep 5
$ ./myScript
<ctrl-c here>
$ echo $?
130

Ich bin mir nicht sicher, wie ich die anderen Signale testen würde ...

$ ./myScript &
$ killall myScript
$ echo $?
0  # duh, that's the exit code of killall
$ killall -9 myScript
$ echo $?
0  # same problem
60
Cory Klein

Prozesse können den Systemaufruf _exit() (unter Linux siehe auch exit_group()) mit einem ganzzahligen Argument aufrufen, um einen Exit-Code an das übergeordnete Element zu melden. Obwohl es sich um eine Ganzzahl handelt, stehen dem übergeordneten Element nur die 8 niedrigstwertigen Bits zur Verfügung (Ausnahme: Verwenden von waitid() oder eines Handlers auf SIGCHLD im übergeordneten Element, um diesen Code abzurufen nicht unter Linux).

Die Eltern führen normalerweise eine wait() oder waitpid() aus, um den Status ihres Kindes als Ganzzahl () zu erhalten. obwohl waitid() mit etwas anderer Semantik ebenfalls verwendet werden kann).

Wenn der Prozess unter Linux und den meisten Unices normal beendet wird, enthalten die Bits 8 bis 15 dieser Statusnummer den an exit(). Wenn nicht, enthalten die 7 niedrigstwertigen Bits (0 bis 6) die Signalnummer und Bit 7 wird gesetzt, wenn ein Kern entleert wurde.

Perl 's $? enthält beispielsweise die durch waitpid() festgelegte Zahl:

$ Perl -e 'system q(kill $$); printf "%04x\n", $?'
000f # killed by signal 15
$ Perl -e 'system q(kill -ILL $$); printf "%04x\n", $?'
0084 # killed by signal 4 and core dumped
$ Perl -e 'system q(exit $((0xabc))); printf "%04x\n", $?'
bc00 # terminated normally, 0xbc the lowest 8 bits of the status

Bourne-ähnliche Shells machen auch den Exit-Status des letzten Ausführungsbefehls in ihrer eigenen Variablen $?. Es enthält jedoch nicht direkt die von waitpid() zurückgegebene Nummer, sondern eine Transformation darauf, und es unterscheidet sich zwischen Shells.

Allen Shells ist gemeinsam, dass $? Die niedrigsten 8 Bits des Exit-Codes enthält (die an exit() übergebene Nummer), wenn der Prozess normal beendet wurde.

Wo es sich unterscheidet, ist, wenn der Prozess durch ein Signal beendet wird. In allen Fällen, und das wird von POSIX verlangt, ist die Anzahl größer als 128. POSIX gibt nicht an, wie hoch der Wert sein kann. In der Praxis enthalten jedoch in allen mir bekannten Bourne-ähnlichen Shells die niedrigsten 7 Bits von $? Die Signalnummer. Aber wobei n die Signalnummer ist,

  • in ash, zsh, pdksh, bash, der Bourne Shell, ist $?128 + n. Das bedeutet, dass Sie in diesen Shells, wenn Sie einen $? Von 129 Erhalten, nicht wissen, ob dies daran liegt, dass der Prozess mit exit(129) beendet wurde oder ob dies der Fall war durch das Signal 1 getötet (HUP auf den meisten Systemen). Das Grundprinzip ist jedoch, dass Shells, wenn sie sich selbst beenden, standardmäßig den Beendigungsstatus des zuletzt beendeten Befehls zurückgeben. Wenn Sie sicherstellen, dass $? Niemals größer als 255 ist, können Sie einen konsistenten Exit-Status erhalten:

    $ bash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    bash: line 1: 16720 Terminated              sh -c "kill \$\$"
    8f # 128 + 15
    $ bash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
    bash: line 1: 16726 Terminated              sh -c "kill \$\$"
    8f # here that 0x8f is from a exit(143) done by bash. Though it's
       # not from a killed process, that does tell us that probably
       # something was killed by a SIGTERM
    
  • ksh93, $? Ist 256 + n. Das bedeutet, dass Sie ab einem Wert von $? Zwischen einem getöteten und einem nicht getöteten Prozess unterscheiden können. Neuere Versionen von ksh beenden sich beim Beenden, wenn $? Größer als 255 war, mit demselben Signal, um dem übergeordneten Status denselben Beendigungsstatus melden zu können. Das klingt zwar nach einer guten Idee, bedeutet aber, dass ksh einen zusätzlichen Core-Dump generiert (der möglicherweise den anderen überschreibt), wenn der Prozess durch ein Core-Generierungssignal beendet wurde:

    $ ksh -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    ksh: 16828: Terminated
    10f # 256 + 15
    $ ksh -c 'sh -c "kill -ILL \$\$"; exit'; printf '%x\n' "$?"
    ksh: 16816: Illegal instruction(coredump)
    Illegal instruction(coredump)
    104 # 256 + 15, ksh did indeed kill itself so as to report the same
        # exit status as sh. Older versions of `ksh93` would have returned
        # 4 instead.
    

    Man könnte sogar sagen, dass es einen Fehler gibt, dass ksh93 Sich selbst tötet, selbst wenn $? Von einem return 257 Stammt, der von einer Funktion ausgeführt wird:

    $ ksh -c 'f() { return "$1"; }; f 257; exit'
    zsh: hangup     ksh -c 'f() { return "$1"; }; f 257; exit'
    # ksh kills itself with a SIGHUP so as to report a 257 exit status
    # to its parent
    
  • yash . yash bietet einen Kompromiss. Es wird 256 + 128 + n Zurückgegeben. Das heißt, wir können auch zwischen einem getöteten und einem ordnungsgemäß beendeten Prozess unterscheiden. Und beim Beenden wird 128 + n Gemeldet, ohne sich selbst und die möglichen Nebenwirkungen umbringen zu müssen.

    $ yash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    18f # 256 + 128 + 15
    $ yash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
    8f  # that's from a exit(143), yash was not killed
    

Um das Signal vom Wert von $? Zu erhalten, verwenden Sie kill -l:

$ /bin/kill 0
Terminated
$ kill -l "$?"
TERM

(Aus Gründen der Portabilität sollten Sie niemals Signalnummern verwenden, sondern nur Signalnamen.)

Auf den Nicht-Bourne-Fronten:

  • csh/tcsh und fish wie die Bourne-Shell, außer dass der Status in $status anstelle von $? steht (beachten Sie, dass zsh setzt auch $status für die Kompatibilität mit csh (zusätzlich zu $?).
  • rc: Der Exit-Status befindet sich ebenfalls in $status, aber wenn diese Variable durch ein Signal beendet wird, enthält sie den Namen des Signals (wie sigterm oder sigill+core wenn ein Kern generiert wurde) anstelle einer Zahl, was ein weiterer Beweis für das gute Design dieser Shell ist.
  • es. Der Exit-Status ist keine Variable. Wenn Sie sich dafür interessieren, führen Sie den Befehl wie folgt aus:

    status = <={cmd}
    

    dies gibt eine Zahl oder sigterm oder sigsegv+core wie in rc zurück.

Der Vollständigkeit halber sollten wir die Arrays zsh$pipestatus Und bash$PIPESTATUS Erwähnen, die den Exit-Status der Komponenten der letzten Pipeline enthalten.

Der Vollständigkeit halber werden Funktionen für Shell-Funktionen und Quelldateien standardmäßig mit dem Exit-Status des letzten Befehls ausgeführt, können aber auch explizit mit dem integrierten return einen Rückgabestatus festlegen. Und wir sehen hier einige Unterschiede:

  • bash und mksh (seit R41, eine anscheinend absichtlich eingeführte Regression ^ Wchange ) kürzen die Zahl (positiv oder negativ) auf 8 Bit. So setzt beispielsweise return 1234$? Auf 210, return -- -1 Auf $? Auf 255.
  • zsh und pdksh (und andere Ableitungen als mksh) erlauben jede vorzeichenbehaftete 32-Bit-Dezimalzahl (-2)31 zu 231-1) (und kürzen Sie die Zahl auf 32 Bit).
  • ash und yash erlauben jede positive ganze Zahl von 0 bis 231-1 und geben einen Fehler für eine beliebige Zahl davon zurück.
  • ksh93 Für return 0 Auf return 320 Setzen Sie $? So wie es ist, aber für alles andere auf 8 Bit kürzen. Beachten Sie, wie bereits erwähnt, dass die Rückgabe einer Zahl zwischen 256 und 320 dazu führen kann, dass sich ksh beim Beenden selbst tötet.
  • rc und es erlauben die Rückgabe von Listen.

Beachten Sie auch, dass einige Shells auch spezielle Werte von $?/$status Verwenden, um einige Fehlerbedingungen zu melden, die nicht den Exit-Status eines Prozesses darstellen, z. B. 127 Oder 126 für Befehl nicht gefunden oder nicht ausführbar (oder Syntaxfehler in eine bezogene Datei) ...

64

Wenn ein Prozess beendet wird, gibt er einen ganzzahligen Wert an das Betriebssystem zurück. Bei den meisten Unix-Varianten wird dieser Wert modulo 256 verwendet: Alles außer den niederwertigen Bits wird ignoriert. Der Status eines untergeordneten Prozesses wird über eine 16-Bit-Ganzzahl an den übergeordneten Prozess zurückgegeben

  • die Bits 0–6 (die 7 niederwertigen Bits) sind die Signalnummer, die zum Beenden des Prozesses verwendet wurde, oder 0, wenn der Prozess normal beendet wurde.
  • bit 7 wird gesetzt, wenn der Prozess durch ein Signal und einen entleerten Kern beendet wurde;
  • die Bits 8–15 sind der Exit-Code des Prozesses, wenn der Prozess normal beendet wurde, oder 0, wenn der Prozess durch ein Signal beendet wurde.

Der Status wird vom Systemaufruf wait oder einem seiner Geschwister zurückgegeben. POSIX gibt nicht die genaue Codierung des Exit-Status und der Signalnummer an. es bietet nur

  • eine Möglichkeit festzustellen, ob der Ausgangsstatus einem Signal oder einem normalen Ausgang entspricht;
  • eine Möglichkeit, auf den Exit-Code zuzugreifen, wenn der Prozess normal beendet wurde;
  • eine Möglichkeit, auf die Signalnummer zuzugreifen, wenn der Prozess durch ein Signal abgebrochen wurde.

Genau genommen gibt es keinen Exit-Code , wenn ein Prozess durch ein Signal beendet wird: Stattdessen gibt es einen Exit-Code . Status .

In einem Shell-Skript wird Exit-Status eines Befehls über die spezielle Variable $? . Diese Variable codiert den Exit-Status mehrdeutig:

  • Wenn der Prozess normal beendet wurde, dann $? ist der Exit-Status.
  • Wenn der Prozess durch ein Signal beendet wurde, dann $? ist 128 plus die Signalnummer auf den meisten Systemen. POSIX schreibt nur vor, dass $? ist in diesem Fall größer als 128; ksh93 fügt 256 statt 128 hinzu. Ich habe noch nie eine Unix-Variante gesehen, die etwas anderes getan hat, als der Signalnummer eine Konstante hinzuzufügen.

Daher können Sie in einem Shell-Skript nicht abschließend feststellen, ob ein Befehl durch ein Signal beendet oder mit einem Statuscode größer als 128 beendet wurde, außer mit ksh93. Es ist sehr selten, dass Programme mit Statuscodes größer als 128 beendet werden, auch weil Programmierer dies aufgrund des $? Mehrdeutigkeit.

SIGINT ist bei den meisten Unix-Varianten Signal 2, also $? ist 128 + 2 = 130 für einen Prozess, der von SIGINT beendet wurde. Sie sehen 129 für SIGHUP, 137 für SIGKILL usw.

Das hängt von Ihrer Shell ab. In der Manpage bash(1), Shell GRAMMAR Abschnitt, Einfache Befehle Unterabschnitt:

Der Rückgabewert eines einfachen Befehls ist [...] 128 + n wenn der Befehl durch das Signal n beendet wird.

Da SIGINT auf Ihrem System Signal Nummer 2 ist, beträgt der Rückgabewert 130, wenn es unter Bash ausgeführt wird.

Es scheint der richtige Ort zu sein, um zu erwähnen, dass SVr4 1989 waitid () eingeführt hat, aber bisher scheint es kein wichtiges Programm zu verwenden. Mit waitid () können die vollen 32 Bits aus dem exit () - Code abgerufen werden.

Vor ungefähr 2 Monaten habe ich den Wait/Job Control-Teil der Bourne Shell neu geschrieben, um waitid () anstelle von waitpid () zu verwenden. Dies wurde durchgeführt, um die Einschränkung zu beseitigen, die den Exit-Code mit 0xFF maskiert.

Die waitid () - Schnittstelle ist viel sauberer als frühere wait () - Implementierungen, mit Ausnahme des Aufrufs cwait () von UNOS aus dem Jahr 1980.

Vielleicht möchten Sie die Manpage unter folgender Adresse lesen:

http://schillix.sourceforge.net/man/man1/bosh.1.html

und überprüfen Sie den Abschnitt "Parametersubstitution", der derzeit auf Seite 8 angezeigt wird.

Die neuen Variablen .sh. * Wurden für die Schnittstelle waitid () eingeführt. Diese Schnittstelle hat keine mehrdeutigen Bedeutungen mehr für die für $? und machen die Schnittstelle viel einfacher.

Beachten Sie, dass Sie eine POSIX-kompatible waitid () benötigen, um diese Funktion nutzen zu können. Mac OS X und Linux bieten dies derzeit nicht an, aber die waitid () wird beim Aufruf von waitpid () emuliert Bei Nicht-POSIX-Plattformen erhalten Sie immer noch nur 8 Bit aus dem Exit-Code.

Kurz gesagt: .sh.status ist der numerische Exit-Code, .sh.code ist der numerische Exit-Grund.

Zur besseren Portabilität gibt es: .sh.codename für die Textversion des Exit-Grundes, z. "DUMPED" und .sh.termsig, der Singularname für das Signal, das den Prozess beendet hat.

Zur besseren Verwendung gibt es zwei nicht exitbezogene .sh.codename-Werte: "NOEXEC" und "NOTFOUND", die verwendet werden, wenn ein Programm überhaupt nicht gestartet werden kann.

FreeBSD hat den Kerlnel-Fehler waitid () innerhalb von 20 Stunden nach meinem Bericht behoben. Linux hat noch nicht mit dem Fix begonnen. Ich hoffe, dass 26 Jahre nach der Einführung dieser Funktion, die jetzt in POSIX verfügbar ist, alle Betriebssysteme sie bald korrekt unterstützen werden.

3
schily