Path: utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!apple!usc!zaphod.mps.ohio-state.edu!wuarchive!udel!haven!mimsy!chris From: chris@mimsy.umd.edu (Chris Torek) Newsgroups: comp.unix.questions Subject: Re: Funny csh output? Message-ID: <22955@mimsy.umd.edu> Date: 7 Mar 90 10:21:43 GMT References: <6662@cps3xx.UUCP> <12251@smoke.BRL.MIL> <6669@cps3xx.UUCP> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 102 >>In article <6662@cps3xx.UUCP> davisd@cpsvax.cps.msu.edu (Dug) asks why `jobs | wc' produces `0 0 0' while `jobs' by itself produces at least one line of output. >In article <12251@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) provides a terse but correct answer: >>There are no suspended jobs in the shell that's running the pipeline >>(it's a subprocess of the one that printed the prompt). In article <6669@cps3xx.UUCP> davisd@cpsvax.cps.msu.edu (Dug) asks further: >But it that's true then why can I redirect it to a file and have my >stopped jobs show up there? (i.e. jobs > test will result in "test" >having the correct output of jobs) To understand all of this you need to know several things: first, what makes something a `job'; second, how the shell manages jobs; and third, how the shell manages pipes and file redirection. A `job' is a collection of processes (a `pipeline'), all of which must be direct descendents of the process that controls them. For instance, given % sleep 1000 | cat | tr -d X & the C shell is the immediate parent of three processes, one a `sleep 1000', one a `cat', and one a `tr -d X'. These three are arranged in a pipeline via a great deal of shell magic. The shell manages the jobs via signals and the `wait3' (now `wait4') system call, and (initially) through `process groups'. Only the immediate parent of the job can do this, because only the immediate parent of a process can wait for that process (wait3()/wait4()). The shell creates a sub- process with the `fork' system call: after the shell forks, the child is no longer the immediate parent of any jobs that the parent created. (The parent, however, *is* the immediate parent of the child, and thus the child process is added to the list the parent maintains.) Since the child is not the parent of the other processes, it must forget about them. Now, to create the pipeline `a | b', the shell does the following: step 0: call pipe() to create a pipe. step 1: call fork() to create a subprocess. 1.1: in the child, close stdout, make the write end of the pipe stdout, close the read end of the pipe (not needed), and then exec process `a'. child part of shell is now gone (parent can now continue if it used vfork()). [various details about setpgrp() left out here.] 1.2: in the parent, close the write end of the pipe (no longer needed). record the child process ID as part of the new job. step 2: call fork() to create a subprocess. 2.1: in the child, close stdin, make the read end of the pipe stdin, and then exec process `b'. [more details about setpgrp() left out here.] 2.2: in the parent, close the read end of the pipe. record the child process ID as part of the new job. Now suppose that `b' is anything, but `a' is the `jobs' command. In this case, step 1.1 does not do the exec() to run part a, but rather simply prints out its list of jobs. But wait! This code is run by a child process, and a child process is not the direct parent of any jobs the shell is running, so it has already forgotten about them. Hence, it lists no jobs. Well, what if we run `jobs > foo' instead of `jobs | cat'? This time the shell only has to run one command, so instead of forking, it squirrels away its current stdout, opens the output file `foo', does the task at hand (here printing jobs, not fork-and-exec'ing some other program), and then moves stdout back. (In actuality, the shell cheats and never moves the file descriptors at all, except once when it first starts up.) This time the jobs-printing is done by the original shell; it does not forget about any jobs. Now, the shell could be more clever, and not forget its jobs even when it forks, and then `jobs | cat' would do something useful. But the shell is just not that clever. Incidentally, the C shell gets very confused when you pipe into a built-in: % cat | jobs [1] + Running cat | % Reset tty pgrp from 2710 to 1281 % jobs [1] Hangup cat | % cat | echo % Reset tty pgrp from 2711 to 1281 j [1] Hangup cat | % It does not matter what you pipe into or out of, as long as the last thing in the pipeline is a built-in. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris