United States |
Previous | Contents | Index |
|
Example 4-1 shows how the signal , alarm , and pause functions operate. It also shows how to establish a signal handler to catch a signal, thereby preventing program termination.
Example 4-1 Suspending and Resuming Programs |
---|
/* CHAP_4_SUSPEND_RESUME.C */ /* This program shows how to alternately suspend and resume a */ /* program using the signal, alarm, and pause functions. */ #define SECONDS 5 #include <stdio.h> #include <signal.h> int number_of_alarms = 5; /* Set alarm counter. */ void alarm_action(int); main() { signal(SIGALRM, alarm_action); /* Establish a signal handler. */ /* to catch the SIGALRM signal.*/ alarm(SECONDS); /* Set alarm clock for 5 seconds. */ pause(); /* Suspend the process until * * the signal is received. */ } void alarm_action(int x) { printf("\t<%d\007>", number_of_alarms); /* Print the value of */ /* the alarm counter. */ signal(SIGALRM, alarm_action); /* Reset the signal. */ alarm(SECONDS); /* Set the alarm clock. */ if (--number_of_alarms) /* Decrement alarm counter. */ pause(); } |
Here is the sample output from Example 4-1:
$ RUN EXAMPLE <5> <4> <3> <2> <1> |
The Compaq C Run-Time Library (RTL) provides functions that allow you to create subprocesses from a Compaq C program. The creating process is called the parent and the created subprocess is called the child.
To create a child process within the parent process, use the exec functions ( execl , execle , execv , execve , execlp , and execvp ) and the vfork function. Other functions are available to allow the parent and child to read and write data across processes ( pipe ) and to allow for synchronization of the two processes ( wait ). This chapter describes how to implement and use these functions.
The parent process can execute Compaq C programs in its children, either synchronously or asynchronously. The number of children that can run simultaneously is determined by the /PRCLM user authorization quota established for each user on your system. Other quotas that may affect the use of subprocesses are /ENQLM (Queue Entry Limit), /ASTLM (AST Waits Limit), and /FILLM (Open File Limit).
This chapter discusses the subprocess functions. Table 5-1 lists and describes all the subprocess functions found in the Compaq C RTL. For more detailed information on each function, see the Reference Section.
Function | Description |
---|---|
Implementation of Child Processes | |
system | Passes a given string to the host environment to be executed by a command processor. |
vfork | Creates an independent child process. |
The exec Functions | |
execl, execle, execlp
execv, execve, execvp |
Passes the name of the image to be activated in a child process. |
Synchronizing Process | |
wait , wait3 , wait4 , waitpid , | Suspends the parent process until a value is returned from a child. |
Interprocess Communication | |
pipe | Allows for communication between a parent and child. |
Child processes are created by Compaq C functions with the OpenVMS LIB$SPAWN RTL routine. (See the VMS Run-Time Library Routines Volume for information on LIB$SPAWN.) Using LIB$SPAWN allows you to create multiple levels of child processes; the parent's children can also spawn children, and so on, up to the limits allowed by the user authorization quotas discussed in the introductory section of this chapter.
Child processes can only execute other Compaq C programs. Other native-mode OpenVMS languages do not share the ability of Compaq C to communicate between processes; if they do, they do not use the same mechanisms. The parent process must be run under a Compaq supported command-language interpreter (CLI), such as the DIGITAL Command Language (DCL). You cannot run the parent as a detached process or under control of a user-supplied CLI.
Parent and child processes communicate through a mailbox as shown in Figure 5-1. This mailbox transfers the context in which the child will run. This context mailbox passes information to the child that it inherits from the parent, such as the names and file descriptors of all the files opened by the parent and the current location within those files. The mailbox is deleted by the parent when the child image exits.
Figure 5-1 Communications Links Between Parent and Child Processes
The mailbox created by the vfork and exec functions is temporary. The logical name of this mailbox is VAXC$EXECMBX and is reserved for use by the Compaq C RTL. |
The mailbox is created with a maximum message size and a buffer quota of 512 bytes each. You need the TMPMBX privilege to create a mailbox with these RTL functions. Since TMPMBX is the privilege required by the DCL commands PRINT and SUBMIT, most users on a system have this privilege. To see what system privileges you have, enter a SHOW PROCESS/PRIVILEGES command.
You cannot change the characteristics of these mailboxes. For more
information on mailboxes, see the VMS I/O User's Reference Volume.
5.2 The exec Functions
There are six exec functions that you can call to execute a Compaq C image in the child process. These functions expect that vfork has been called to set up a return address. The exec functions will call vfork if the parent process did not.
When vfork is called by the parent, the exec function returns to the parent process. When vfork was called by an exec function, the exec function returns to itself, waits for the child to exit, and then exits the parent process. The exec function does not return to the parent process unless the parent calls vfork to save the return address.
In OpenVMS Version 7.2, the exec functions were enhanced to activate either executable images or DCL command procedures. If no file extension is specified in the file_name argument, the functions first search for the file with the .EXE file extension and then for the file with the .COM file extension. If both the executable image and the command procedure with the same name exist, you must explicitly specify the .COM file extension to force activating the command procedure.
For a DCL command procedure, the exec functions pass the first eight arg0, arg1, ..., arguments specified in the exec call to the command procedure as P1, P2, ... parameters, preserving the case.
Unlike UNIX based systems, the exec functions in the Compaq C RTL cannot always determine if the specified executable image or command procedure exists and can be activated and executed. Therefore, the exec functions might appear to succeed even though the specified file cannot be executed by the child process.
The status of the child process, returned to the parent process, indicates that the error occurred. You can retrieve this error code by using one of the functions from the wait family of functions.
The vfork and exec functions in the Compaq C RTL on OpenVMS systems work differently than on UNIX systems:
For a progammer, the key differences are:
|
The exec functions use the LIB$SPAWN routine to create the subprocess and activate the child image within the subprocess. This child process inherits the parent's environment, including all defined logical names and command-line interpreter symbols. The exec functions use the logical name VAXC$EXECMBX to communicate between parent and child; this logical name must not exist outside the context of the parent image.
The exec functions pass the following information to the child:
When everything is transmitted to the child, exec processing is complete. Control in the parent process then returns to the address saved by vfork and the child's process ID is returned to the parent.
See also Section 4.2.4 for a discussion of signal actions and the
SIGCHLD signal.
5.2.2 Exec Error Conditions
The exec functions will fail if LIB$SPAWN cannot create the subprocess. Conditions that can cause a failure include exceeding the subprocess quota or finding the communications by the context mailbox between the parent and child to be broken. Exceeding some quotas will not cause LIB$SPAWN to fail, but will put LIB$SPAWN into a wait state that can cause the parent process to hang. An example of such a quota is the Open File Limit quota.
You will need an Open File Limit quota of at least 20 files, with an average of three times the number of concurrent processes that your program will run. If you use more than one open pipe at a time, or perform I/O on several files at one time, this quota may need to be even higher. See your system manager if this quota needs to be increased.
When an exec function fails, a value of --1 is returned. After such a
failure, the parent is expected to call either the
exit
or
_exit
function. Both functions then return to the parent's
vfork
call, which returns the child's process ID. In this case, the child
process ID returned by exec is less than zero. For more information
about the
exit
function, see the Reference Section.
5.3 Synchronizing Processes
A child process is terminated when the parent process terminates.
Therefore, the parent process must check the status of its child
processes before exiting. This is done using the Compaq C RTL
function
wait
.
5.4 Interprocess Communication
A channel through which parent and child processes communicate is
called a pipe. Use the
pipe
function to create a pipe.
5.5 Program Examples
Example 5-1 shows the basic procedures for executing an image in a child process. The child process in Example 5-1 prints a message 10 times.
Example 5-1 Creating the Child Process |
---|
/* chap_5_exec_image.c */ /* This example creates the child process. The only */ /* functionality given to the child is the ability to */ /* print a message 10 times. */ #include <climsgdef.h> /* CLI status values */ #include <stdio.h> #include <perror.h> #include <processes.h> #include <stdlib.h> static const char *child_name = "chap_5_exec_image_child.exe" ; main() { int status, cstatus; /* NOTE: */ /* Any local automatic variables, even those */ /* having the volitile attribute, may have */ /* indeterminant values if they are modified */ /* between the vfork() call and the matching */ /* exec() call. */ if ((status = vfork()) != 0) { /* This is either an error or */ /* the "second" vfork return, taking us "back" */ /* to parent mode. */ if (status < 0) printf("Parent - Child process failed\n"); else { printf("Parent - Waiting for Child\n"); if ((status = wait(&cstatus)) == -1) perror("Parent - Wait failed"); else if (cstatus == CLI$_IMAGEFNF) printf("Parent - Child does not exist\n"); else printf("Parent - Child final status: %d\n", cstatus); } } else { /* The FIRST Vfork return is zero, do the exec() */ printf("Parent - Starting Child\n"); if ((status = execl(child_name, 0)) == -1) { perror("Parent - Execl failed"); exit(EXIT_FAILURE); } } } ---------------------------------------------------------- |
/* CHAP_5_EXEC_IMAGE_CHILD.C */ /* This is the child program that writes a message */ /* through the parent to "stdout" */ #include <stdio.h> main() { int i; for (i = 0; i < 10; i++) printf("Child - executing\n"); return (255) ; /* Set an unusual success stat */ } |
Key to Example 5-1:
In Example 5-2, the parent passes arguments to the child process.
Example 5-2 Passing Arguments to the Child Process |
---|
/* CHAP_5_CHILDARG.C */ /* In this example, the arguments are placed in an array, gargv, */ /* but they can be passed to the child explicitly as a zero- */ /* terminated series of character strings. The child program in this */ /* example writes the arguments that have been passed it to stdout. */ #include <climsgdef.h> #include <stdio.h> #include <stdlib.h> #include <perror.h> #include <processes.h> const char *child_name = "chap_5_childarg_child.exe" ; main() { int status, cstatus; char *gargv[] = {"Child", "ARGC1", "ARGC2", "Parent", 0}; if ((status = vfork()) != 0) { if (status < -1) printf("Parent - Child process failed\n"); else { printf("Parent - waiting for Child\n"); if ((status = wait(&cstatus)) == -1) perror("Parent - Wait failed"); else if (cstatus == CLI$_IMAGEFNF) printf("Parent - Child does not exist\n"); else printf("Parent - Child final status: %x\n", cstatus); } } else { printf("Parent - Starting Child\n"); if ((status = execv(child_name, gargv)) == -1) { perror("Parent - Exec failed"); exit(EXIT_FAILURE); } } } -------------------------------------------------------- /* CHAP_5_CHILDARG_CHILD.C */ /* This is a child program that echos its arguments */ #include <stdio.h> main(argc, argv) int argc; char *argv[]; { int i; printf("Program name: %s\n", argv[0]); for (i = 1; i < argc; i++) printf("Argument %d: %s\n", i, argv[i]); return(255) ; } |
Example 5-3 shows how to use the wait function to check the final status of multiple children being run simultaneously.
Example 5-3 Checking the Status of Child Processes |
---|
/* CHAP_5_CHECK_STAT.C */ /* In this example 5 child processes are started. The wait() */ /* function is placed in a separate for loop so that it is */ /* called once for each child. If wait() were called within */ /* the first for loop, the parent would wait for one child to */ /* terminate before executing the next child. If there were */ /* only one wait request, any child still running when the */ /* parent exits would terminate prematurely. */ #include <climsgdef.h> #include <stdio.h> #include <stdlib.h> #include <perror.h> #include <processes.h> const char *child_name = "chap_5_check_stat_child.exe" ; main() { int status, cstatus, i; for (i = 0; i < 5; i++) { if ((status = vfork()) == 0) { printf("Parent - Starting Child %d\n", i); if ((status = execl(child_name, 0)) == -1) { perror("Parent - Exec failed"); exit(EXIT_FAILURE); } } else if (status < -1) printf("Parent - Child process failed\n"); } printf("Parent - Waiting for children\n"); for (i = 0; i < 5; i++) { if ((status = wait(&cstatus)) == -1) perror("Parent - Wait failed"); else if (cstatus == CLI$_IMAGEFNF) printf("Parent - Child does not exist\n"); else printf("Parent - Child %X final status: %d\n", status, cstatus); } } |
Example 5-4 shows how to use the pipe and dup2 functions to communicate between a parent and child process through specific file descriptors. The #define preprocessor directive defines the preprocessor constants inpipe and outpipe as the names of file descriptors 11 and 12.
Example 5-4 Communicating Through a Pipe |
---|
/* CHAP_5_PIPE.C */ /* In this example, the parent writes a string to the pipe for */ /* the child to read. The child then writes the string back */ /* to the pipe for the parent to read. The wait function is */ /* called before the parent reads the string that the child has */ /* passed back through the pipe. Otherwise, the reads and */ /* writes will not be synchronized. */ #include <perror.h> #include <climsgdef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <processes.h> #include <unixio.h> #define inpipe 11 #define outpipe 12 const char *child_name = "chap_5_pipe_child.exe" ; main() { int pipes[2]; int mode, status, cstatus, len; char *outbuf, *inbuf; if ((outbuf = malloc(512)) == 0) { printf("Parent - Outbuf allocation failed\n"); exit(EXIT_FAILURE); } if ((inbuf = malloc(512)) == 0) { printf("Parent - Inbuf allocation failed\n"); exit(EXIT_FAILURE); } if (pipe(pipes) == -1) { printf("Parent - Pipe allocation failed\n"); exit(EXIT_FAILURE); } dup2(pipes[0], inpipe); dup2(pipes[1], outpipe); strcpy(outbuf, "This is a test of two-way pipes.\n"); status = vfork(); switch (status) { case 0: printf("Parent - Starting child\n"); if ((status = execl(child_name, 0)) == -1) { printf("Parent - Exec failed"); exit(EXIT_FAILURE); } break; case -1: printf("Parent - Child process failed\n"); break; default: printf("Parent - Writing to child\n"); if (write(outpipe, outbuf, strlen(outbuf) + 1) == -1) { perror("Parent - Write failed"); exit(EXIT_FAILURE); } else { if ((status = wait(&cstatus)) == -1) perror("Parent - Wait failed"); if (cstatus == CLI$_IMAGEFNF) printf("Parent - Child does not exist\n"); else { printf("Parent - Reading from child\n"); if ((len = read(inpipe, inbuf, 512)) <= 0) { perror("Parent - Read failed"); exit(EXIT_FAILURE); } else { printf("Parent: %s\n", inbuf); printf("Parent - Child final status: %d\n", cstatus); } } } break; } } ------------------------------------------------------------------ /* CHAP_5_PIPE_CHILD.C */ /* This is a child program which reads from a pipe and writes */ /* the received message back to its parent. */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #define inpipe 11 #define outpipe 12 main() { char *buffer; int len; if ((buffer = malloc(512)) == 0) { perror("Child - Buffer allocation failed\n"); exit(EXIT_FAILURE); } printf("Child - Reading from parent\n"); if ((len = read(inpipe, buffer, 512)) <= 0) { perror("Child - Read failed"); exit(EXIT_FAILURE); } else { printf("Child: %s\n", buffer); printf("Child - Writing to parent\n"); if (write(outpipe, buffer, strlen(buffer) + 1) == -1) { perror("Child - Write failed"); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); } |
Previous | Next | Contents | Index |
|