Betrachten Sie Folgendes (mit sh
Sein /bin/dash
):
$ strace -e trace=process sh -c 'grep "^Pid:" /proc/self/status /proc/$$/status'
execve("/bin/sh", ["sh", "-c", "grep \"^Pid:\" /proc/self/status /"...], [/* 47 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7fcc8b661540) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fcc8b661810) = 24865
wait4(-1, /proc/self/status:Pid: 24865
/proc/24864/status:Pid: 24864
[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 24865
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=24865, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
exit_group(0) = ?
+++ exited with 0 +++
Es gibt nichts Ungewöhnliches, grep
ersetzt einen gegabelten Prozess (hier über clone()
) durch den Haupt-Shell-Prozess. So weit, ist es gut.
Jetzt mit Bash 4.4:
$ strace -e trace=process bash -c 'grep "^Pid:" /proc/self/status /proc/$$/status'
execve("/bin/bash", ["bash", "-c", "grep \"^Pid:\" /proc/self/status /"...], [/* 47 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f8416b88740) = 0
execve("/bin/grep", ["grep", "^Pid:", "/proc/self/status", "/proc/25798/status"], [/* 47 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f8113358b80) = 0
/proc/self/status:Pid: 25798
/proc/25798/status:Pid: 25798
exit_group(0) = ?
+++ exited with 0 +++
Hier ist offensichtlich, dass grep
PID des Shell-Prozesses und kein offensichtlicher fork()
oder clone()
Aufruf vorausgesetzt wird . Die Frage ist also, wie man bash
eine solche Akrobatik ohne einen der Aufrufe erreicht.
Beachten Sie jedoch, dass clone()
syscalls angezeigt wird, wenn der Befehl eine Shell-Umleitung enthält, zdf > /dev/null
grep
.bash
ist möglicherweise intelligent genug, um nicht zu verzweigen, wenn nur ein einziger externer Befehl ausgeführt wird.CMD_NO_FORK
execve
da ersleep
eingebaut ist, aber Homers Antwort beantwortet ihn angemessen, aber auch hier nur eine kurze Zusammenfassung. Ich hätte wohl erwähnen sollen, dass ich nach exakten Mechanismen auf Quellcode-Ebene gesucht habe. Würden Sie sagen, dass meine Antwort geeignet ist, Homers in diesem Beitrag zu unterstützen? Es macht mir nichts aus, wenn dieser Beitrag geschlossen wird. Ich habe die Antwort so oder so bekommenAntworten:
Das
sh -c 'command line'
ist in der Regel durch die Dinge verwendet , wiesystem("command line")
,ssh host 'command line'
,vi
‚s!
,cron
und generell alles , was verwendet wird , eine Befehlszeile zu interpretieren, so ist es ziemlich wichtig ist es so effizient wie möglich zu machen.Forking ist teuer, in Bezug auf CPU-Zeit, Speicher, zugewiesene Dateideskriptoren ... Es ist nur eine Verschwendung von Ressourcen, wenn ein Shell-Prozess vor dem Beenden nur auf einen anderen Prozess wartet. Außerdem ist es schwierig, den Exit-Status des separaten Prozesses, der den Befehl ausführen würde, korrekt zu melden (z. B. wenn der Prozess beendet wird).
Viele Schalen versuchen im Allgemeinen, die Anzahl der Gabeln als Optimierung zu minimieren. Sogar nicht optimierte Schalen mögen
bash
es in densh -c cmd
oder(cmd in subshell)
Fällen. Im Gegensatz zu ksh oder zsh funktioniert dies nicht inbash -c 'cmd > redir'
oderbash -c 'cmd1; cmd2'
(dasselbe in Unterschalen). ksh93 ist der Prozess, bei dem Gabeln am weitesten vermieden werden.Es gibt Fälle, in denen diese Optimierung nicht durchgeführt werden kann, z.
Wo
sh
kann die Verzweigung für den letzten Befehl nicht übersprungen werden, da möglicherweise mehr Text an das Skript angehängt wird, während dieser Befehl ausgeführt wird. Bei nicht durchsuchbaren Dateien kann das Dateiende nicht erkannt werden, da dies bedeuten kann, dass zu viel zu früh aus der Datei gelesen wird.Oder:
Wo die Shell möglicherweise mehr Befehle ausführen muss, nachdem der "letzte" Befehl ausgeführt wurde.
quelle
Durch Durchsuchen des Bash-Quellcodes konnte ich herausfinden, dass Bash das Gabeln tatsächlich ignoriert, wenn es keine Pipes oder Umleitungen gibt. Ab Zeile 1601 in execute_cmd.c :
Später gehen diese Flags in die
execute_disk_command()
Funktion über, wodurch die Ganzzahlvariable nofork eingerichtet wird, die später überprüft wird, bevor versucht wird, sie zu forken . Der eigentliche Befehl selbst wird von derexecve()
Wrapper- Funktion shell_execve () entweder vom gegabelten oder vom übergeordneten Prozess ausgeführt, und in diesem Fall ist es der eigentliche übergeordnete Prozess.Der Grund für eine solche Mechanik ist in Stephanes Antwort gut erklärt .
Randnotiz außerhalb des Rahmens dieser Frage: Es sollte beachtet werden, dass es anscheinend darauf ankommt, ob die Shell interaktiv ist oder über läuft
-c
. Vor dem Ausführen des Befehls befindet sich eine Gabelung. Dies ergibt sich aus der Ausführungstrace
auf der interaktiven Shell (strace -e trace=process -f -o test.trace bash
) und dem Überprüfen der Ausgabedatei:Siehe auch Warum erzeugt bash keine Unterschale für einfache Befehle?
quelle