1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
|
A basic tt(Fork) class should hide all bookkeeping details of a system
call like tt(fork) from its users. The class tt(Fork) developed here does
just that. The class itself only ensures the proper execution of the tt(fork)
system call. Normally, tt(fork) is called to start a child process, usually
boiling down to the execution of a separate process. This child process may
expect input at its standard input stream and/or may generate output to its
standard output and/or standard error streams. tt(Fork) does not know all
this, and does not have to know what the child process will do. tt(Fork)
objects should be able to start their child processes.
tt(Fork)'s constructor cannot know what actions its child
process should perform. Similarly, it cannot know what actions the parent
process should perform. For these kind of situations, the
emi(template method design pattern)
hi(design pattern: template method)
was developed. According to Gamma c.s., the em(template method design
pattern)
quote(
``Define(s) the skeleton of an algorithm in an operation, deferring some
steps to subclasses. [The] Template Method (design pattern) lets
subclasses redefine certain steps of an algorithm, without changing
the algorithm's structure.''
)
This design pattern allows us to define an emi(abstract base class)
hi(base class)
already providing the essential steps related to the tt(fork) system call,
deferring the implementation of other parts of the tt(fork) system call to
subclasses.
The tt(Fork) abstract base class has the following characteristics:
itemization(
it() It defines a data member tt(d_pid). In the parent process this data
member contains the child's emi(process id) and in the child process it has
the value 0. Its public interface declares only two members:
itemization(
it() a ti(fork) member function, responsible for the actual forking (i.e.,
it creates the (new) child process);
it() a tt(virtual) destructor tt(~Fork) (having an empty body).
)
Here is tt(Fork)'s interface:
verbinclude(//CLASS examples/fork.h)
it() All other non-virtual member functions are declared in the class's
tt(protected) section and can thus em(only) be used by derived classes. They
are:
itemization(
itt(pid()): The member function tt(pid) allows derived classes to
access the system tt(fork)'s return value:
verbinclude(//PID examples/fork.h)
itt(waitForChild()): The member tt(int waitForChild) can be called by
parent processes to wait for the completion of their child
processes (as discussed below). This member is declared in the
class interface. Its implementation is:
verbinclude(-a examples/waitforchild.cc)
This simple implementation returns the child's emi(exit status) to
the parent. The called system function ti(waitpid) em(blocks)
until the child terminates.
)
it() When tt(fork) system calls are used,
em(parent processes) hi(parent process)
and
em(child processes) hi(child process)
must always be distinguished. The main distinction between these
processes is that tt(d_pid) becomes the child's process-id in the
parent process, while tt(d_pid) becomes 0 in the child process
itself. Since these two processes must always be distinguished (and
present), their implementation by classes derived from tt(Fork) is
enforced by tt(Fork)'s interface: the members tt(childProcess),
defining the child process' actions and tt(parentProcess), defining
the parent process' actions were defined as pure virtual functions.
it() communication between parent- and child processes
may use standard streams or other facilities, like em(pipes)
(cf. section ref(PIPE)). To facilitate this inter-process
communication, derived classes em(may) implement:
itemization(
itt(childRedirections()): this member should be overridden by derived
classes if any standard stream (tt(cin, cout,)) or tt(cerr) must
be redirected in the em(child) process (cf. section
ref(REDIRECTION)). By default it has an empty implementation;
itt(parentRedirections()): this member should be overridden by derived
classes if any standard stream (tt(cin, cout,)) or tt(cerr) must
be redirected in the em(parent) process. By default it has an
empty implementation.
)
Redirection of the standard streams is necessary if parent and
child processes must communicate with each other via the standard streams.
Here are their default definitions. Since these functions are virtual
functions they should not be implemented inline, but in their own source file:
verbinclude(//REDIRECT examples/forkvirtual.cc)
)
|