tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: subshell differences between /bin/sh and /bin/ksh



    Date:        Sat, 8 Apr 2017 07:44:01 +0000
    From:        "Johnny C. Lam" <jlam%NetBSD.org@localhost>
    Message-ID:  <20170408074401.GA15473%homeworld.netbsd.org@localhost>

  | 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)




Home | Main Index | Thread Index | Old Index