Looking for tcsh binary which uses vfork

Chris Torek chris at mimsy.UUCP
Sun Sep 10 02:11:28 AEST 1989


In article <246 at paralogics.UUCP> shaw at paralogics.UUCP (Guy Shaw) writes:
>Chris Torek didn't seem to be saying that vfork() caused incorrect
>behavior, only that there is something better.

Actually, it causes (indirectly) correct behaviour.

The C shell runs a pipeline such as

	a | b | c

by doing the sequence

	<<make pipe 1>>
	if ((pgroup = vfork()) == 0) {
		<<move pipe 1 to stdout>>
		<<set tty pgroup and process group>>
		<<exec a>>
	}
	<<make pipe 2>>
	if (vfork() == 0) {
		<<move pipe 1 to stdin>>
		<<move pipe 2 to stdout>>
		<<set process group>>
		<<exec b>>
	}
	<<close pipe 1>>
	if (vfork() == 0) {
		<<move pipe 2 to stdin>>
		<<set process group>>
		<<exec c>>
	}
	<<close pipe 2>>

Since vfork() suspends the execution of the parent process until the
child process either exec()s or exit()s, the `set tty pgroup' happens
before any of the three child processes actually start running.

Fork(), however, does *not* suspend the parent process, and suddenly
we have a race to see whether the tty pgroup gets set in time.

>>The accepted solution is to set the terminal's process group k+1 times
>>when there are k children in a pipeline (or k times with the current
>>system): once in each child and once in the parent.  Setting the pgroup
>>to whatever it is already is harmless, and this ensures that the pgroup
>>is set by the time it needs to be.

>Do you mean do a right-to-left series of TIOCSPGRP ioctl calls,
>as well as setpgrp calls?

Since there is a race, the order is (and must be) irrelevant.

The Bourne shell happens to fork and exec in such an order that,
in `a | b | c', the shell is the parent of processes `a' and `c',
but process c is the parent of process b.  The C shell (since it
wants to do job control) makes sure that all three are direct
descendents of the shell itself, and happens to fork `a' first,
`b' second, and `c' third.

>I take it that, the way things are now, process "a"
>is the only one that bothers with a TIOCSPGRP.  Sorry if I misunderstand
>this, I have no source.

This is correct, but the source itself is unnecessary in this case.  We
can deduce this from the `jobs' command:

	% sleep 1 | sleep 10 | sleep 30 &
	[1] 300 301 302
	% jobs -l
	[1]  -   300 Done		  sleep 1 |
		 301 Running		  sleep 10 |
		 302			  sleep 30
	%
	% sleep 10; jobs
	[1]  - Done		    sleep 1 | sleep 10 |
	       Running		    sleep 30
	% sleep 20; jobs
	[1]  - Done		    sleep 1 | sleep 10 | sleep 30
	%

This tells us that csh is the direct parent of each element of the
pipeline (since it gets status updates when each child exits).  If we
assume that csh uses vfork() (and is deterministic), we can deduce that
csh vfork()s once, the child runs the first command, the parent
unsticks and vfork()s again, the child runs the second command, etc.
The child of the first vfork() must exec the first command, or the
`sleep 1' above would not have the first process ID.

Since the first child of vfork is guaranteed to run first, it must
be `a' that sets the tty pgroup (given that the tty pgroup is set
only once).
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris at mimsy.umd.edu	Path:	uunet!mimsy!chris



More information about the Comp.unix.wizards mailing list