Cmput 379 Lab Exercise #3
For the period 26 January - 1 February, 2001
Objective
Write a C-program to explore the use of:
-
fork() -- for creating child processes
-
pipe() -- for communicating between two processes that have a
"parent-child" relationship
-
one of the six different exec() system calls to replace the parent.
-
wait() / waitpid() -- for collecting <defunct>
processes, and to avoid problems with "zombies".
Filenames
Hold the source code of the driver program in ParentChild.c and
place the source of your "to be executed" child in Exec.c
Program Review (previous week's work)
During the second week you should have written a small program
along the following lines:
The parent is in an infinite loop reading lines of data until an EOF
occurs. When data arrives at stdin a child process is spawned
and the data passed to it via a pipe, perhaps using the "write"
system call to send N bytes The child reads the line of data from
the pipe, but since it does not know how many bytes have been put there
some care is needed. The following approaches are possible:
-
Have the parent first transmit the line length to
the child, and then transmit the actual line
-
Have the child use dup2 to attach its stdin to the
pipe from the parent, and then use fdopen() and fgets() for reading.
fgets() could be used in conjunction with realloc() or, for a more brutal
approach, with a 4096-byte array (the maximum capacity of a pipe).
-
Have the child read one byte at a time until the
end-of-line marker is found
The child also closes its stdout descriptor
and uses dup2 to attach stdout to a second pipe. Now the child can
use all the output functions that are normally associated with stdout to
send the converted data back to the parent.
Issues that could arise:
When executed as a command line of the form ParentChild there
is no problem. When executed as:
ParentChild <infile >outfile
there may be a problem if infile is large
(has very long or varied-length lines).
Children will be created in rapid succession and, depending on how the
i/o is done and when the context switches occur, a later initiated child
process may output some of its characters before an earlier
process. There should not be any lost data.
In every lab, students should demonstrate any
program with anomalous i/o behaviour to their TA, and should propose explanations
for the anomaly.
Program Guidelines (this week's work)
Re-implement last week's model program so that the Child Process now invokes
the executable that is compiled from the source Exec.c This is done
using one of the exec() system functions. The replacement process
will have the same process ID as the Child. The replacement must
receive from the original child all the file descriptors it needs. The
new Child must read from the Parent's
outlet pipe via stdin, and must attach
its stdout to the inlet pipe back to the parent
(thus replicating the communication path of the week #2 exercise).
Program Execution
It should be possible to invoke your program in the following ways:
ParentChild
ParentChild <infile >outfile
ParentChild <infile 2>&outfile
The last example is for C-shell users and combines stdout and stderr
output into a single file. An equivalent demonstration by sh, bash
and tsh users is expected.
While executing ParentChild <infile >outfile students should type Ctrl-Z and
then issue a 'ps' command to see what processes exist. Comment on what you see.
Restart with the fg (foreground) command.
Requirements
-
Exec.c receives from the original child the "descriptions"
of the inlet and outlet pipes that the parent is using [Stevens,
on p.210, says that by default open file descriptors are preserved across
an exec call, but you can also transfer these descriptors explicitly in
the exec() parameter list]. The
Exec.c child attaches the parent's outlet pipe to its own stdin, and its
own stdout to the parent's inlet pipe, by using dup2() twice.
As in Lab #2, Exec.c converts the input data to upper case but now returns
to the parent several lines of output. Each line represents a single
word of text from the input line. White space is the word separator.
In
other words, the child code from Exercise #2 should be transferred into
Exec.c (and enhanced by having each word delimited by '\n'). The
fork child code only has to setup the file descriptors correctly [using
dup2()],
and then 'exec' the Exec.c executable.
-
It is necessary for students to demonstrate that
their program terminates naturally and without loss of data when an EOF
is read by the parent. At the end of every line sent by the Parent
to the Child the outlet pipe should be closed, even though this may not
be strictly necessary. This allows the Child to see the resultant
EOF on the pipe (not actually important here, since the Child is only reading
a single line). However, because the Child is now responding to the
Parent with multiple lines of output, the Child must close its outlet pipe
to the Parent, so that the Parent in turn can see the EOF there and know
that communication from the Child is complete.
-
One fundamental problem that is being addressed is
for the Parent to know that it has sent an EOF to the Child, and thus that
when the last Child terminates (e.g., the inlet pipe from the Child also
shows EOF, or the wait()/waitpid() function completes), the Parent
itself exits instead looping back to read more data.
Marking Guide
20 marks for completion of Lab Exercise #1 and #2
by 25 January (that is at the end of the lab period, or as the TA designates).
10 marks for week three, use of exec() to launch
a program that returns each word on a separate line.
10 marks for week three, having the parent use
wait() / waitpid() to clean-up zombies before exiting.
In the case of exercise #3,
a portion of the marks will be for active
participation in lab. sessions and "quality" of work done:
clean structure of programs, appropriate
(but not excessive) comments, good programming style.
Copyright ©
Department of Computing Science.
All rights reserved.
Last update:
|