When using Upstart, controlling subprocesses (child process) is quite important. But what confused me is as following, which has gone beyond upstart itself:
scenario 1:
root@ubuntu-jstorm:~/Desktop# su cr -c 'sleep 20 > /tmp/a.out'
I got 3 processes by: cr@ubuntu-jstorm:~$ ps -ef | grep -v grep | grep sleep
root 8026 6544 0 11:11 pts/2 00:00:00 su cr -c sleep 20 > /tmp/a.out
cr 8027 8026 0 11:11 ? 00:00:00 bash -c sleep 20 > /tmp/a.out
cr 8028 8027 0 11:11 ? 00:00:00 sleep 20
scenario 2:
root@ubuntu-jstorm:~/Desktop# su cr -c 'sleep 20'
I got 2 processes by: cr@ubuntu-jstorm:~$ ps -ef | grep -v grep | grep sleep
root 7975 6544 0 10:03 pts/2 00:00:00 su cr -c sleep 20
cr 7976 7975 0 10:03 ? 00:00:00 sleep 20
The process of sleep 20
is the one I care, especially in Upstart, the process managed by Upstart should be this while not bash -c sleep 20 > /tmp/a.out
is managed by Upstart, while not the sleep 20
.
In scenario 1, upstart will not work correctly, above is the reason.
Therefore, why scenario 1 got 3 process, this doesn't make sense for me. Even though I know I can use command 'exec' to fix it, I just want to get the procedure what happened when the two command committed.
su -c
starts the shell and passes it the command via its -c
option. The shell may spawn as many processes as it likes (it depends on the given command).
It appears the shell executes the command directly without forking in some cases e.g., if you run su -c '/bin/sleep $$'
then the apparent behaviour as if:
su
starts a shell process (e.g., /bin/sh
)$$
with itexec()
/bin/sleep
.You should see in ps
output that sleep
's argument is equal to its pid in this case.
If you run su -c '/bin/sleep $$ >/tmp/sleep'
then /bin/sleep
argument is different from its PID (it is equal to the ancestor's PID) i.e.:
su
starts a shell process (e.g., /bin/sh
)$$
with itexec()
/bin/sleep
.The double fork indicates that the actual sequence of events might be different e.g., su
could orchestrate the forking or not forking, not the shell (I don't know). It seems the double fork is there to make sure that the command won't get a controlling terminal.