Funny csh output?
Chris Torek
chris at mimsy.umd.edu
Wed Mar 7 20:21:43 AEST 1990
>>In article <6662 at cps3xx.UUCP> davisd at 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 at smoke.BRL.MIL> gwyn at 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 at cps3xx.UUCP> davisd at 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 at cs.umd.edu Path: uunet!mimsy!chris
More information about the Comp.unix.questions
mailing list