Aby zrozumieć ten problem, trzeba się dowiedzieć jak bash
wykonuje polecenia przekazywane do niej w wierszu poleceń. Kluczową kwestią jest to, że jeśli komenda jest na tyle prosty, nie ma fork
(lub clone
lub coś podobnego).
$ strace -f -e clone,execve /bin/bash -c 'cat /proc/$$/cmdline'
execve("/bin/bash", ["/bin/bash", "-c", "cat /proc/$$/cmdline"], [/* 80 vars */]) = 0
execve("/bin/cat", ["cat", "/proc/2942/cmdline"], [/* 80 vars */]) = 0
cat/proc/2942/cmdline+++ exited with 0 +++
$
OTOH jeżeli polecenie jest bardziej skomplikowana, bash
widelce:
$ strace -f -e clone,execve /bin/bash -c 'echo $$; cat /proc/$$/cmdline'
execve("/bin/bash", ["/bin/bash", "-c", "echo $$; cat /proc/$$/cmdline"], [/* 80 vars */]) = 0
2933
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7ff64e6779d0) = 2934
Process 2934 attached
[pid 2934] execve("/bin/cat", ["cat", "/proc/2933/cmdline"], [/* 80 vars */]) = 0
/bin/bash-cecho $$; cat /proc/$$/cmdline[pid 2934] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=2934, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
$
Wydaje się, $$ jest PID kota zamiast PID bash/sh jest.
To rzeczywiście oba. bash
execve
s cat
bezpośrednio, więc jeden staje się drugim.
Aby zrozumieć, co dokładnie jest potrzebne do no-widelec zachowania, musimy spojrzeć na kod źródłowy. Jest to komentarz:
/*
* IF
* we were invoked as `bash -c' (startup_state == 2) AND
* parse_and_execute has not been called recursively AND
* we're not running a trap AND
* we have parsed the full command (string == '\0') AND
* we're not going to run the exit trap AND
* we have a simple command without redirections AND
* the command is not being timed AND
* the command's return status is not being inverted
* THEN
* tell the execution code that we don't need to fork
*/
Source
Widziałem to wyjście kiedy straced równie dobrze. Gdziekolwiek stwierdzi, że polecenie nie jest skomplikowane i po tokenizacji określa, że nie musi odpalać żadnego podprocesu (w wyniku ';' lub '||' lub '&&' lub '|') to po prostu ' execve's przekazać polecenie do niego. W przeciwnym razie 'clone's, a następnie' execve's w potomku/klonie. To, czego nie rozumiem, to dlaczego. – ffledgling
@ffledgling Nie jest komentarz w źródle 'IF byliśmy wywołany jako \' bash -c”(startup_state == 2) I parse_and_execute nie została wywołana rekurencyjnie i nie jesteśmy prowadzenie pułapkę i mamy przeanalizował pełną komendę (ciąg == '\ 0') ORAZ nie będziemy uruchamiać pułapki wyjściowej ORAZ mamy proste polecenie bez przekierowań ORAZ komenda nie jest odmierzana ORAZ status powrotu komendy nie jest jest odwrócony THEN THEN powiedzieć kod wykonawczy, że nie trzeba rozwidlenia ' –
Czy mógłbyś dodać to do odpowiedzi i link do odpowiedniej linii w oficjalnym źródłem? – ffledgling