I understand that when I run exit
it terminates my current shell because exit
command run in the same shell. I also understand that when I run exit &
then original shell will not terminate because &
ensures that the command is run in sub-shell resulting that exit
will terminate this sub-shell and return back to original shell. But what I do not understand is why commands with and without &
looks exactly the same under pstree
, in this case sleep 10
and sleep 10 &
. 4669 is the PID of bash under which first sleep 10
and then sleep 10 &
were issued and following output was obtained from another shell instance during this time:
# version without &
$ pstree 4669
bash(4669)───sleep(6345)
# version with &
$ pstree 4669
bash(4669)───sleep(6364)
Should't the version with &
contain one more spawned sub-shell (e.g. in this case with PID 5555), like this one?
bash(4669)───bash(5555)───sleep(6364)
PS: Following code was omitted from output of pstree
beginning for better readability:
systemd(1)───slim(1009)───ck-launch-sessi(1370)───openbox(1551)───/usr/bin/termin(4510)───bash(4518)───screen(4667)───screen(4668)───
Best Answer
Until I started answering this question, I hadn’t realised that using the
&
control operator to run a job in the background starts a subshell. Subshells are created when commands are wrapped in parentheses or form part of a pipeline (each command in a pipeline is executed in its own subshell).The Lists of Commands section of the Bash manual (thanks jimmij) states:
As I understand it, when you run
sleep 10 &
the shell forks to create a new child process (a copy of itself) and then immediately execs to replace this child process with code from the external command (sleep
). This is similar to what happens when a command is run as normal (in the foreground). See the Fork–exec Wikipedia article for a short overview of this mechanism.I couldn’t understand why Bash would run backgrounded commands in a subshell but it makes sense if you also want to be able to run shell builtins such as
exit
orecho
to be run in the background (not just external commands).When it’s a shell builtin that’s being run in the background, the
fork
happens (resulting in a subshell) without anexec
call to replace itself with an external command. Running the following commands shows that when theecho
command is wrapped in curly braces and run in the background (with the&
), a subshell is indeed created:In the above example, I wrapped the
echo
command in curly braces to avoidBASH_SUBSHELL
being expanded by the current shell; curly braces are used to group commands together without using a subshell. The second version of the command (ending with the&
control operator) clearly demonstrates that terminating the command with the ampersand has resulted in a subshell (with a new PID) being created to execute theecho
builtin. (I’m probably simplifying the shell’s behaviour here. See mikeserv’s comment.)I would never have thought of running
exit &
and had I not read your question, I would have expected the current shell to quit. Knowing now that such commands are run in a subshell, your explanation that it’s the subshell which exits makes sense.“Why is subshell created by background control operator (&) not displayed under pstree”
As mentioned above, when you run
sleep 10 &
, Bash forks itself to create the subshell but sincesleep
is an external command, it calls theexec()
system call which immediately replaces the Bash code and data in the child process with a running copy of thesleep
program. By the time you runpstree
, theexec
call will already have completed and the child process will now have the name “sleep”.While away from my computer, I tried to think of a way of keeping the subshell running long enough for the subshell to be displayed by
pstree
. I figured we could run the command through thetime
builtin:Here, the Bash shell (26793) forks to create a subshell (4502) in order to execute the command in the background. This subshell runs its own
time
builtin command which, in turn, forks (to create a new process with PID 4503) and execs to run the externalsleep
command.Using named pipes, jimmij came up with a clever way to keep the subshell created to run
exit
alive long enough for it to be displayed bypstree
:Redirecting
stdin
from a named pipe is clever as it causes the subshell to block until it receives input from the named pipe. Later, redirecting the output ofecho
(without any arguments) writes a newline character to the named pipe which unblocks the subshell process which, in turn, runs theexit
builtin command.Similarly, for the
sleep
command:Here we see that the subshell created to run the command in the background has a PID of
6600
. Next, we unblock the process by writing a newline character to the pipe:The subshell then
exec
s to run thesleep
command.After the
exec()
call, we can see that the child process (6600
) is now running thesleep
program.