Date: Sat, 8 Apr 2017 07:44:01 +0000 From: "Johnny C. Lam" <j...@netbsd.org> Message-ID: <20170408074401.ga15...@homeworld.netbsd.org>
| Could someone please explain the output below? I have the following | shell script to inspect the PIDs of child processes: Our /bin/sh is (from long ago) somewhat conservative in creating processes, to make it obviously correct, but not as optimised as it could be. | In the "sleep twice in subshell" (3rd) case, why is there another | subshell process with PID 3239? The process chain looks like: | | /bin/sh t.sh > /bin/sh t.sh > /bin/sh t.sh > /bin/sleep 1 There's one process for the subshell (), then one for the command list inside it. And yes, that's not really necessary, but it makes things nice and clean and consistent... Note that the 2nd of those shells does not fork again for the 2nd sleep, if you repeated the ps again while the 2nd sleep was running, there would be one less shell, once the list is exhausted, no more shell forking is needed... I append output below that shows that, I changed the end of the script to be ps -o "pid,ppid,command" sleep 1.2 ps -o "pid,ppid,command" wait | In the "sleep in subshell" (2nd) case, why is there no subshell? | | /bin/sh t.sh > /bin/sleep 1 As Edgar said, some shells optimise process creation (some shells have had much more attention over time than ours has had). There's no real need for an extra shell there, the () don't change anything wrt the sematics, so a shell that is smart enough can avoid a fork in that case. Actually, in the examples you gave, a truly smart shell could avoid forkig at all (other than to exec sleep and ps) everything else could be handled in one shell process - but that's only because the command sequences are very simple, there is nothing in your script that affects the state of the shell (no var assignments, no I/O redirections, etc). Our shell makes no attempt to be that smart (or smart at all) - just simple and correct (at least in this area.) Here's the output from the modified script (with the extra ps command) (this is using a 7.0 BETA /bin/sh - but nothing has changed in this area in a long time ... from reading the sources I see that there have been past attempts to elide some forks, but they all apparently caused problems, and have been removed). >>> sleep > parent: 3059 > child: 648 PID PPID COMMAND 648 3059 /bin/sleep 1 3059 16588 /bin/sh /tmp/S 13618 3059 ps -o pid,ppid,command >>> sleep in subshell > parent: 3059 > child: 5988 PID PPID COMMAND 3059 16588 /bin/sh /tmp/S 5988 3059 /bin/sh /tmp/S 11880 3059 ps -o pid,ppid,command 14405 5988 /bin/sleep 1 >>> sleep twice in subshell > parent: 3059 > child: 12123 PID PPID COMMAND 3059 16588 /bin/sh /tmp/S 12123 3059 /bin/sh /tmp/S 13521 29823 /bin/sleep 1 19948 3059 ps -o pid,ppid,command 29823 12123 /bin/sh /tmp/S PID PPID COMMAND 3059 16588 /bin/sh /tmp/S 5636 3059 ps -o pid,ppid,command 12123 3059 /bin/sh /tmp/S 29823 12123 /bin/sleep 2 (I removed the parent shell that ran all this from all of that, it just clogged up the output.) You will see at the end pid 29823 (the sleep 2) was /bin/sh in the immediately preceding ps output. And for anyone that really cares, here is trace output from a debug version of the shell (this one closer to the NetBSD current shell, but again, aside from the tracing, which is totally different, there are no changes in the process creation area) that shows (to some degree) what the shell is doing. (This is from an earlier run before he extra sleep;ps was added to the script) Note that the "echo" commands don't appear here, and the "wait" commands only related to their side-effect of actually waiting, because no processes are created to run those commands (I could have enabled more tracing so they would be more visible, but they are not really important...) so I have inserted blank lines at the places just after the first two "wait" commands, where the following 'echo >>>' commands appear. The hash marks over near the pid's show the number of subshells deep the particular instance of the shell making the tracing output is. ANd just in case anyone wonders "wasroot" in the output has nothing whatever to do with uid 0 (it relates to the root of the tree of commands). kre Tracing started. Shell args: "../sh" "/tmp/S" 24953: makejob(0x62c8c0, 1) returns %1 24953: Made job for background command 27585=# Child shell 27585 (vforked) wasroot parent=24953 27585=# tryexec /bin/sleep [vforked] 24953: commandtext: ps->cmd f7b0e408, end f7b0e416, left 46 24953: "/bin/sleep 1 &" 24953: In parent shell(24953): bg child = 27585 [mode:1] 24953: makejob(0x62c8f0, 1) returns %2 24953: Made job for foreground command 4809=# Child shell 4809 (vforked) wasroot parent=24953 4809=# tryexec /bin/ps [vforked] 24953: commandtext: ps->cmd f7b0e4f0, end f7b0e508, left 36 24953: "ps -o "pid,ppid,command"" 24953: In parent shell(24953): fg child = 4809 [mode:0] 24953: waitforjob(%2) called 24953: dowait(1) called 24953: wait (in 24953) returns pid 4809, status 0 24953: Job %2: changing status of proc 4809 from 0xffffffff to 0x0 24953: Job %2: changing state from 0 to 2 24953: waitforjob: job 2, nproc 1, status 0, st 0 24953: dowait(1) called 24953: wait (in 24953) returns pid 27585, status 0 24953: Job %1: changing status of proc 27585 from 0xffffffff to 0x0 24953: Job %1: changing state from 0 to 2 24953: showjob: freeing job 1 24953: makejob(0x62c8f0, 1) returns %1 24953: forkshell(%1, 0x62c8f0, 1) called 24953: commandtext: ps->cmd f7b0e408, end f7b0e416, left 46 24953: "(/bin/sleep 1)" 24953: In parent shell(24953): bg child = 13455 [mode:1] 24953: makejob(0x62c8f0, 1) returns %2 24953: Made job for foreground command 19936=# Child shell 19936 (vforked) wasroot parent=24953 19936=# tryexec /bin/ps [vforked] 24953: commandtext: ps->cmd f7b0e4f0, end f7b0e508, left 36 24953: "ps -o "pid,ppid,command"" 24953: In parent shell(24953): fg child = 19936 [mode:0] 24953: waitforjob(%2) called 24953: dowait(1) called 13455=# Child shell 13455 (forked) wasroot parent=24953 13455=# makejob(0x62c868, 1) returns %1 13455=# forkshell(%1, 0x62c868, 0) called 13455=# commandtext: ps->cmd f7b0e408, end f7b0e416, left 46 13455=# "(/bin/sleep 1)" 13455=# In parent shell(13455): fg child = 10592 [mode:0] 13455=# waitforjob(%1) called 13455=# dowait(1) called 10592=## Child shell 10592 (forked) parent=13455 10592=## tryexec /bin/sleep [] 24953: wait (in 24953) returns pid 19936, status 0 24953: Job %2: changing status of proc 19936 from 0xffffffff to 0x0 24953: Job %2: changing state from 0 to 2 24953: waitforjob: job 2, nproc 1, status 0, st 0 24953: dowait(1) called 13455=# wait (in 13455) returns pid 10592, status 0 13455=# Job %1: changing status of proc 10592 from 0xffffffff to 0x0 13455=# Job %1: changing state from 0 to 2 13455=# waitforjob: job 1, nproc 1, status 0, st 0 13455=# pid 13455, exitshell(0) 24953: wait (in 24953) returns pid 13455, status 0 24953: Job %1: changing status of proc 13455 from 0xffffffff to 0x0 24953: Job %1: changing state from 0 to 2 24953: showjob: freeing job 1 24953: makejob(0x62c978, 1) returns %1 24953: forkshell(%1, 0x62c978, 1) called 24953: commandtext: ps->cmd f7b0e408, end f7b0e424, left 32 24953: "(/bin/sleep 1; /bin/sleep 2)" 24953: In parent shell(24953): bg child = 22097 [mode:1] 24953: makejob(0x62c8f0, 1) returns %2 24953: Made job for foreground command 12912=# Child shell 12912 (vforked) wasroot parent=24953 12912=# tryexec /bin/ps [vforked] 24953: commandtext: ps->cmd f7b0e4f0, end f7b0e508, left 36 24953: "ps -o "pid,ppid,command"" 24953: In parent shell(24953): fg child = 12912 [mode:0] 24953: waitforjob(%2) called 24953: dowait(1) called 22097=# Child shell 22097 (forked) wasroot parent=24953 22097=# makejob(0x62c868, 1) returns %1 22097=# forkshell(%1, 0x62c868, 0) called 22097=# commandtext: ps->cmd f7b0e408, end f7b0e424, left 32 22097=# "(/bin/sleep 1; /bin/sleep 2)" 22097=# In parent shell(22097): fg child = 16846 [mode:0] 22097=# waitforjob(%1) called 22097=# dowait(1) called 16846=## Child shell 16846 (forked) parent=22097 16846=## makejob(0x62c8d8, 1) returns %1 16846=## Made job for foreground command 4378=### Child shell 4378 (vforked) parent=16846 4378=### tryexec /bin/sleep [vforked] 16846=## commandtext: ps->cmd f7b0e408, end f7b0e414, left 48 16846=## "/bin/sleep 1" 16846=## In parent shell(16846): fg child = 4378 [mode:0] 16846=## waitforjob(%1) called 16846=## dowait(1) called 24953: wait (in 24953) returns pid 12912, status 0 24953: Job %2: changing status of proc 12912 from 0xffffffff to 0x0 24953: Job %2: changing state from 0 to 2 24953: waitforjob: job 2, nproc 1, status 0, st 0 24953: dowait(1) called 16846=## wait (in 16846) returns pid 4378, status 0 16846=## Job %1: changing status of proc 4378 from 0xffffffff to 0x0 16846=## Job %1: changing state from 0 to 2 16846=## waitforjob: job 1, nproc 1, status 0, st 0 16846=## tryexec /bin/sleep [] 22097=# wait (in 22097) returns pid 16846, status 0 22097=# Job %1: changing status of proc 16846 from 0xffffffff to 0x0 22097=# Job %1: changing state from 0 to 2 22097=# waitforjob: job 1, nproc 1, status 0, st 0 22097=# pid 22097, exitshell(0) 24953: wait (in 24953) returns pid 22097, status 0 24953: Job %1: changing status of proc 22097 from 0xffffffff to 0x0 24953: Job %1: changing state from 0 to 2 24953: showjob: freeing job 1 24953: pid 24953, exitshell(0)