aboutsummaryrefslogtreecommitdiff
path: root/share/doc/psd/04.uprog/p5
diff options
context:
space:
mode:
Diffstat (limited to 'share/doc/psd/04.uprog/p5')
-rw-r--r--share/doc/psd/04.uprog/p5574
1 files changed, 574 insertions, 0 deletions
diff --git a/share/doc/psd/04.uprog/p5 b/share/doc/psd/04.uprog/p5
new file mode 100644
index 000000000000..e75720d82828
--- /dev/null
+++ b/share/doc/psd/04.uprog/p5
@@ -0,0 +1,574 @@
+.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are
+.\" met:
+.\"
+.\" Redistributions of source code and documentation must retain the above
+.\" copyright notice, this list of conditions and the following
+.\" disclaimer.
+.\"
+.\" Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\"
+.\" This product includes software developed or owned by Caldera
+.\" International, Inc. Neither the name of Caldera International, Inc.
+.\" nor the names of other contributors may be used to endorse or promote
+.\" products derived from this software without specific prior written
+.\" permission.
+.\"
+.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+.\" OR OTHERWISE) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\"
+.NH
+PROCESSES
+.PP
+It is often easier to use a program written
+by someone else than to invent one's own.
+This section describes how to
+execute a program from within another.
+.NH 2
+The ``System'' Function
+.PP
+The easiest way to execute a program from another
+is to use
+the standard library routine
+.UL system .
+.UL system
+takes one argument, a command string exactly as typed
+at the terminal
+(except for the newline at the end)
+and executes it.
+For instance, to time-stamp the output of a program,
+.P1
+main()
+{
+ system("date");
+ /* rest of processing */
+}
+.P2
+If the command string has to be built from pieces,
+the in-memory formatting capabilities of
+.UL sprintf
+may be useful.
+.PP
+Remember than
+.UL getc
+and
+.UL putc
+normally buffer their input;
+terminal I/O will not be properly synchronized unless
+this buffering is defeated.
+For output, use
+.UL fflush ;
+for input, see
+.UL setbuf
+in the appendix.
+.NH 2
+Low-Level Process Creation \(em Execl and Execv
+.PP
+If you're not using the standard library,
+or if you need finer control over what
+happens,
+you will have to construct calls to other programs
+using the more primitive routines that the standard
+library's
+.UL system
+routine is based on.
+.PP
+The most basic operation is to execute another program
+.ul
+without
+.IT returning ,
+by using the routine
+.UL execl .
+To print the date as the last action of a running program,
+use
+.P1
+execl("/bin/date", "date", NULL);
+.P2
+The first argument to
+.UL execl
+is the
+.ul
+file name
+of the command; you have to know where it is found
+in the file system.
+The second argument is conventionally
+the program name
+(that is, the last component of the file name),
+but this is seldom used except as a place-holder.
+If the command takes arguments, they are strung out after
+this;
+the end of the list is marked by a
+.UL NULL
+argument.
+.PP
+The
+.UL execl
+call
+overlays the existing program with
+the new one,
+runs that, then exits.
+There is
+.ul
+no
+return to the original program.
+.PP
+More realistically,
+a program might fall into two or more phases
+that communicate only through temporary files.
+Here it is natural to make the second pass
+simply an
+.UL execl
+call from the first.
+.PP
+The one exception to the rule that the original program never gets control
+back occurs when there is an error, for example if the file can't be found
+or is not executable.
+If you don't know where
+.UL date
+is located, say
+.P1
+execl("/bin/date", "date", NULL);
+execl("/usr/bin/date", "date", NULL);
+fprintf(stderr, "Someone stole 'date'\en");
+.P2
+.PP
+A variant of
+.UL execl
+called
+.UL execv
+is useful when you don't know in advance how many arguments there are going to be.
+The call is
+.P1
+execv(filename, argp);
+.P2
+where
+.UL argp
+is an array of pointers to the arguments;
+the last pointer in the array must be
+.UL NULL
+so
+.UL execv
+can tell where the list ends.
+As with
+.UL execl ,
+.UL filename
+is the file in which the program is found, and
+.UL argp[0]
+is the name of the program.
+(This arrangement is identical to the
+.UL argv
+array for program arguments.)
+.PP
+Neither of these routines provides the niceties of normal command execution.
+There is no automatic search of multiple directories \(em
+you have to know precisely where the command is located.
+Nor do you get the expansion of metacharacters like
+.UL < ,
+.UL > ,
+.UL * ,
+.UL ? ,
+and
+.UL []
+in the argument list.
+If you want these, use
+.UL execl
+to invoke the shell
+.UL sh ,
+which then does all the work.
+Construct a string
+.UL commandline
+that contains the complete command as it would have been typed
+at the terminal, then say
+.P1
+execl("/bin/sh", "sh", "-c", commandline, NULL);
+.P2
+The shell is assumed to be at a fixed place,
+.UL /bin/sh .
+Its argument
+.UL -c
+says to treat the next argument
+as a whole command line, so it does just what you want.
+The only problem is in constructing the right information
+in
+.UL commandline .
+.NH 2
+Control of Processes \(em Fork and Wait
+.PP
+So far what we've talked about isn't really all that useful by itself.
+Now we will show how to regain control after running
+a program with
+.UL execl
+or
+.UL execv .
+Since these routines simply overlay the new program on the old one,
+to save the old one requires that it first be split into
+two copies;
+one of these can be overlaid, while the other waits for the new,
+overlaying program to finish.
+The splitting is done by a routine called
+.UL fork :
+.P1
+proc_id = fork();
+.P2
+splits the program into two copies, both of which continue to run.
+The only difference between the two is the value of
+.UL proc_id ,
+the ``process id.''
+In one of these processes (the ``child''),
+.UL proc_id
+is zero.
+In the other
+(the ``parent''),
+.UL proc_id
+is non-zero; it is the process number of the child.
+Thus the basic way to call, and return from,
+another program is
+.P1
+if (fork() == 0)
+ execl("/bin/sh", "sh", "-c", cmd, NULL); /* in child */
+.P2
+And in fact, except for handling errors, this is sufficient.
+The
+.UL fork
+makes two copies of the program.
+In the child, the value returned by
+.UL fork
+is zero, so it calls
+.UL execl
+which does the
+.UL command
+and then dies.
+In the parent,
+.UL fork
+returns non-zero
+so it skips the
+.UL execl.
+(If there is any error,
+.UL fork
+returns
+.UL -1 ).
+.PP
+More often, the parent wants to wait for the child to terminate
+before continuing itself.
+This can be done with
+the function
+.UL wait :
+.P1
+int status;
+
+if (fork() == 0)
+ execl(...);
+wait(&status);
+.P2
+This still doesn't handle any abnormal conditions, such as a failure
+of the
+.UL execl
+or
+.UL fork ,
+or the possibility that there might be more than one child running simultaneously.
+(The
+.UL wait
+returns the
+process id
+of the terminated child, if you want to check it against the value
+returned by
+.UL fork .)
+Finally, this fragment doesn't deal with any
+funny behavior on the part of the child
+(which is reported in
+.UL status ).
+Still, these three lines
+are the heart of the standard library's
+.UL system
+routine,
+which we'll show in a moment.
+.PP
+The
+.UL status
+returned by
+.UL wait
+encodes in its low-order eight bits
+the system's idea of the child's termination status;
+it is 0 for normal termination and non-zero to indicate
+various kinds of problems.
+The next higher eight bits are taken from the argument
+of the call to
+.UL exit
+which caused a normal termination of the child process.
+It is good coding practice
+for all programs to return meaningful
+status.
+.PP
+When a program is called by the shell,
+the three file descriptors
+0, 1, and 2 are set up pointing at the right files,
+and all other possible file descriptors
+are available for use.
+When this program calls another one,
+correct etiquette suggests making sure the same conditions
+hold.
+Neither
+.UL fork
+nor the
+.UL exec
+calls affects open files in any way.
+If the parent is buffering output
+that must come out before output from the child,
+the parent must flush its buffers
+before the
+.UL execl .
+Conversely,
+if a caller buffers an input stream,
+the called program will lose any information
+that has been read by the caller.
+.NH 2
+Pipes
+.PP
+A
+.ul
+pipe
+is an I/O channel intended for use
+between two cooperating processes:
+one process writes into the pipe,
+while the other reads.
+The system looks after buffering the data and synchronizing
+the two processes.
+Most pipes are created by the shell,
+as in
+.P1
+ls | pr
+.P2
+which connects the standard output of
+.UL ls
+to the standard input of
+.UL pr .
+Sometimes, however, it is most convenient
+for a process to set up its own plumbing;
+in this section, we will illustrate how
+the pipe connection is established and used.
+.PP
+The system call
+.UL pipe
+creates a pipe.
+Since a pipe is used for both reading and writing,
+two file descriptors are returned;
+the actual usage is like this:
+.P1
+int fd[2];
+
+stat = pipe(fd);
+if (stat == -1)
+ /* there was an error ... */
+.P2
+.UL fd
+is an array of two file descriptors, where
+.UL fd[0]
+is the read side of the pipe and
+.UL fd[1]
+is for writing.
+These may be used in
+.UL read ,
+.UL write
+and
+.UL close
+calls just like any other file descriptors.
+.PP
+If a process reads a pipe which is empty,
+it will wait until data arrives;
+if a process writes into a pipe which
+is too full, it will wait until the pipe empties somewhat.
+If the write side of the pipe is closed,
+a subsequent
+.UL read
+will encounter end of file.
+.PP
+To illustrate the use of pipes in a realistic setting,
+let us write a function called
+.UL popen(cmd,\ mode) ,
+which creates a process
+.UL cmd
+(just as
+.UL system
+does),
+and returns a file descriptor that will either
+read or write that process, according to
+.UL mode .
+That is,
+the call
+.P1
+fout = popen("pr", WRITE);
+.P2
+creates a process that executes
+the
+.UL pr
+command;
+subsequent
+.UL write
+calls using the file descriptor
+.UL fout
+will send their data to that process
+through the pipe.
+.PP
+.UL popen
+first creates the
+the pipe with a
+.UL pipe
+system call;
+it then
+.UL fork s
+to create two copies of itself.
+The child decides whether it is supposed to read or write,
+closes the other side of the pipe,
+then calls the shell (via
+.UL execl )
+to run the desired process.
+The parent likewise closes the end of the pipe it does not use.
+These closes are necessary to make end-of-file tests work properly.
+For example, if a child that intends to read
+fails to close the write end of the pipe, it will never
+see the end of the pipe file, just because there is one writer
+potentially active.
+.P1
+#include <stdio.h>
+
+#define READ 0
+#define WRITE 1
+#define tst(a, b) (mode == READ ? (b) : (a))
+static int popen_pid;
+
+popen(cmd, mode)
+char *cmd;
+int mode;
+{
+ int p[2];
+
+ if (pipe(p) < 0)
+ return(NULL);
+ if ((popen_pid = fork()) == 0) {
+ close(tst(p[WRITE], p[READ]));
+ close(tst(0, 1));
+ dup(tst(p[READ], p[WRITE]));
+ close(tst(p[READ], p[WRITE]));
+ execl("/bin/sh", "sh", "-c", cmd, 0);
+ _exit(1); /* disaster has occurred if we get here */
+ }
+ if (popen_pid == -1)
+ return(NULL);
+ close(tst(p[READ], p[WRITE]));
+ return(tst(p[WRITE], p[READ]));
+}
+.P2
+The sequence of
+.UL close s
+in the child
+is a bit tricky.
+Suppose
+that the task is to create a child process that will read data from the parent.
+Then the first
+.UL close
+closes the write side of the pipe,
+leaving the read side open.
+The lines
+.P1
+close(tst(0, 1));
+dup(tst(p[READ], p[WRITE]));
+.P2
+are the conventional way to associate the pipe descriptor
+with the standard input of the child.
+The
+.UL close
+closes file descriptor 0,
+that is, the standard input.
+.UL dup
+is a system call that
+returns a duplicate of an already open file descriptor.
+File descriptors are assigned in increasing order
+and the first available one is returned,
+so
+the effect of the
+.UL dup
+is to copy the file descriptor for the pipe (read side)
+to file descriptor 0;
+thus the read side of the pipe becomes the standard input.
+(Yes, this is a bit tricky, but it's a standard idiom.)
+Finally, the old read side of the pipe is closed.
+.PP
+A similar sequence of operations takes place
+when the child process is supposed to write
+from the parent instead of reading.
+You may find it a useful exercise to step through that case.
+.PP
+The job is not quite done,
+for we still need a function
+.UL pclose
+to close the pipe created by
+.UL popen .
+The main reason for using a separate function rather than
+.UL close
+is that it is desirable to wait for the termination of the child process.
+First, the return value from
+.UL pclose
+indicates whether the process succeeded.
+Equally important when a process creates several children
+is that only a bounded number of unwaited-for children
+can exist, even if some of them have terminated;
+performing the
+.UL wait
+lays the child to rest.
+Thus:
+.P1
+#include <signal.h>
+
+pclose(fd) /* close pipe fd */
+int fd;
+{
+ register r, (*hstat)(), (*istat)(), (*qstat)();
+ int status;
+ extern int popen_pid;
+
+ close(fd);
+ istat = signal(SIGINT, SIG_IGN);
+ qstat = signal(SIGQUIT, SIG_IGN);
+ hstat = signal(SIGHUP, SIG_IGN);
+ while ((r = wait(&status)) != popen_pid && r != -1);
+ if (r == -1)
+ status = -1;
+ signal(SIGINT, istat);
+ signal(SIGQUIT, qstat);
+ signal(SIGHUP, hstat);
+ return(status);
+}
+.P2
+The calls to
+.UL signal
+make sure that no interrupts, etc.,
+interfere with the waiting process;
+this is the topic of the next section.
+.PP
+The routine as written has the limitation that only one pipe may
+be open at once, because of the single shared variable
+.UL popen_pid ;
+it really should be an array indexed by file descriptor.
+A
+.UL popen
+function, with slightly different arguments and return value is available
+as part of the standard I/O library discussed below.
+As currently written, it shares the same limitation.