Die Klammern beginnen immer eine Subshell. Was passiert ist, dass Bash erkennt, dass sleep 5
es sich um den letzten Befehl handelt, der von dieser Subshell ausgeführt wird, und exec
statt fork
+ aufruft exec
. Der sleep
Befehl ersetzt die Subshell im selben Prozess.
Mit anderen Worten ist der Basisfall:
( … )
Erstellen Sie eine Unterschale. Der ursprüngliche Prozess ruft fork
und auf wait
. Im Subprozess, der eine Subshell ist:
sleep
ist ein externer Befehl, der einen Unterprozess des Unterprozesses erfordert. Die Subshell ruft fork
und auf wait
. Im Unterprozess:
- Der Unterprozess führt den externen Befehl aus →
exec
.
- Schließlich wird der Befehl beendet →
exit
.
wait
wird in der Unterschale vervollständigt.
wait
wird im ursprünglichen Prozess abgeschlossen.
Die Optimierung ist:
( … )
Erstellen Sie eine Unterschale. Der ursprüngliche Prozess ruft fork
und auf wait
. Im Subprozess, der eine Subshell ist, bis er aufruft exec
:
sleep
ist ein externer Befehl und das Letzte, was dieser Prozess tun muss.
- Der Unterprozess führt den externen Befehl aus →
exec
.
- Schließlich wird der Befehl beendet →
exit
.
wait
wird im ursprünglichen Prozess abgeschlossen.
Wenn Sie nach dem Aufruf von noch etwas hinzufügen sleep
, muss die Subshell beibehalten werden, damit diese Optimierung nicht durchgeführt werden kann.
Wenn Sie vor dem Aufruf von etwas anderes hinzufügen sleep
, könnte die Optimierung durchgeführt werden (und ksh tut es), aber bash tut es nicht (es ist bei dieser Optimierung sehr konservativ).
Gilles 'SO - hör auf böse zu sein'
quelle
fork
und der untergeordnete Prozess wird durch Aufrufen erstellt (um externe Befehle auszuführen)fork + exec
. Aber Ihr erster Absatz legt nahe, dass dies auchfork + exec
als Subshell bezeichnet wird. Was mache ich hier falsch?fork
+exec
wird nicht für die Subshell aufgerufen, sondern für den externen Befehl. Ohne Optimierung gibt es einenfork
Aufruf für die Subshell und einen weiteren für den externen Befehl. Ich habe meiner Antwort eine detaillierte Ablaufbeschreibung hinzugefügt.(...)
(im Basisfall) ein Aufrufexec
von möglicherweise vorliegt oder nicht, abhängig davon, ob die Subshell einen externen Befehl ausführen muss, während im Fall der Ausführung eines externen Befehls ein Aufruf vorliegen mussfork + exec
.date
in einer Shell durchgeführt werden?strace -f -e clone,execve,write bash -c 'date'
undstrace -f -e clone,execve,write bash -c 'date; true'
Im Advanced Bash Programming Guide :
"Im Allgemeinen wird durch einen externen Befehl in einem Skript ein Unterprozess abgebrochen, während dies bei einem integrierten Bash-Befehl nicht der Fall ist. Aus diesem Grund werden integrierte Befehle schneller ausgeführt und benötigen weniger Systemressourcen als die entsprechenden externen Befehle."
Und etwas weiter unten:
Msgstr "Eine in Klammern eingebettete Befehlsliste wird als Subshell ausgeführt."
Beispiele:
Beispiel mit OPs-Code (mit kürzeren Schlafzeiten, weil ich ungeduldig bin):
Die Ausgabe:
quelle
sleep
in einer Subshell ausgeführt wird (möglicherweise während des Subshell-Prozesses, da es sich um einen integrierten und nicht um einen Subprozess der Subshell handelt). In jedem Fall hätte ich jedoch erwartet, dass eine Subshell existiert, dh ein Bash-Unterprozess unter dem übergeordneten Bash-Prozess. Für Snippet B oben scheint dies nicht der Fall zu sein.sleep
dies keinsleep
integrierter Vorgang zu sein scheint, würde ich erwarten, dass der zweite Aufruf in beiden Snippets in einem Unterprozess des Subshell-Prozesses ausgeführt wird.$BASHPID
Variablen zu hacken . Leider hat Ihnen die Art und Weise, wie Sie es taten, nicht die ganze Geschichte erzählt, wie ich glaube. Siehe meine zusätzliche Ausgabe in der Antwort.Eine zusätzliche Anmerkung zu @Gilles Antwort.
Wie Gilles sagte:
The parentheses always start a subshell.
Die Zahlen, die eine solche Unterschale hat, könnten sich jedoch wiederholen:
Wie Sie sehen, wiederholt sich das $$ immer wieder, und das ist wie erwartet, weil (führen Sie diesen Befehl aus, um die richtige
man bash
Zeile zu finden ):Das heißt: Wenn die Shell nicht neu initialisiert wird, ist das $$ dasselbe.
Oder damit:
Das
$$
ist die ID der aktuellen Shell (nicht der Subshell).quelle