Does the Linux shell do a fork/exec and then waitpid() to get the return code to populate the $? variable, each time it executes something?
Asked
Active
Viewed 208 times
1 Answers
3
Yes, that's exactly what it does.
You can see this for yourself, if you are interested, by running the shell under strace
(a tool that intercepts and prints all system calls a program makes).
strace bash -c '/usr/bin/ls > /dev/null; echo $?'
This gives the following output, much trimmed:
execve("/usr/bin/bash", ["bash", "-c", "/usr/bin/ls > /dev/null; echo $?"], [/* 58 vars */]) = 0
[....]
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7ff16c3d69d0) = 1189
[....]
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 1189
[....]
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1189, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, 0x7ffd3096b290, WNOHANG, NULL) = -1 ECHILD (No child processes)
[....]
exit_group(0) = ?
+++ exited with 0 +++
I used a bash script with two commands because otherwise bash doesn't fork but just execs the command, replacing itself (a useful optimization I just discovered it does!)
In the output, clone
is the system call behind the fork
function, and likewise wait4
is called by waitpid
. You don't see the exec
because that happens in the child process, which we didn't ask strace
to trace. If you add -f
then it will do so, but will also trace the contents of the ls
process.

ams
- 24,923
- 4
- 54
- 75
-
Thanks for the answer. Do you know how the value gets passed between processes - is it passed via a register or is there a set memory location used? – Scooter Nov 18 '15 at 12:45
-
When a process exits it calls `_exit(n)` (unless killed by a signal), and the kernel stores the exit code, `n`. The parent process can then retrieve the value using some variant of the `wait` system call. – ams Nov 18 '15 at 12:48