HP OpenVMS Systems

ask the wizard
Content starts here

problems with vfork and pipes?

» close window

The Question is:

 
I'm experimenting with ipc on an alpha (vms 6.2-1h3, decc v5.6-003) and a
vax (vms 6.2, decc v5.6-003).  I've written a very simple program (see
below) that should work, but doesn't and dies in different places
depending on whether I run it on the alpha or the vax.  Here's the
program:
 
 
#include <stdio.h>
#include <processes.h>
#include <unistd.h>
#include <limits.h>
 
#define PARENT_INPUT fd[0]
#define PARENT_OUTPUT fd[1]
 
#define CHILD_INPUT fd[0]
#define CHILD_OUTPUT fd[1]
 
int main (int argc, char *argv[])
 
   int fd[2];
   pid_t pid;
   int n = 0;
   char line[1024];
 
   if (pipe(fd) < 0)
      perror ("pipe");
 
   if ((pid = vfork()) < 0) {
 
      perror("vfork");
 
   } else if (pid > 0) {        /* parent */
 
      fprintf(stderr,"entering parent\n");
 
      if (close (PARENT_OUTPUT) == -1) {
         perror("parent's close");
      } else {
         fprintf(stderr,"parent's close successful\n");
      }
 
      if ((n = read (PARENT_INPUT, line, PIPE_BUF)) <= 0) {
         perror ("parent's read");
      } else {
         fprintf(stderr, "parent's read successful, n = %d\n",n);
      }
 
      if (write(STDOUT_FILENO, line, n) == -1) {
         perror ("write to stdout\n");
      } else {
         fprintf (stderr, "parent write to stdout successful\n");
      }
 
   } else if (pid == 0) {       /* child */
 
      fprintf(stderr,"entering child\n");
 
      if (close (CHILD_INPUT) == -1) {
         perror ("child close");
      } else {
         fprintf(stderr,"child's close successful\n");
      }
 
      if (write (CHILD_OUTPUT, "I'm working!\n",1) == -1) {
         perror ("child's write into pipe");
      } else {
         fprintf (stderr, "child's write into pipe, successful\n");
      }
 
   } else {
 
      fprintf(stderr, "weirdness from vfork(), pid = %d\n", (int) pid);
      perror("pid");
   }
 
   return 1;
 
 
 
On the ALPHA, the output is:
 
entering child
child's close successful
child's write into pipe, successful
%SYSTEM-W-UNWIND, unwind currently in progress
%TRACE-W-TRACEBACK, symbolic stack dump follows
%SYSTEM-W-UNWIND, unwind currently in progress
 
On the VAX the output is:
 
entering child
child's close successful
child's write into pipe, successful
entering parent
parent's close successful
parent's read: bad file number
 
%SYSTEM-F-ROPRAND, reserved operand fault at PC=00002122, PSL=03C00001
%TRACE-F-TRACEBACK, symbolic stack dump follows
module name     routine name                     line       rel PC    abs
PC
 
                                                           00002122
00002122
                                                           00073787
00073787
                                                           00073F66
00073F66
                                                           000749F3
000749F3
                                                           0009FEBD
0009FEBD
TEST            main                             1951      00000105
00000465
 
 
If I comment out the "close" sections in both the parent and the child, it
works (!) on the VAX but continues to die on the ALPHA (output is the same
as above).  On the VAX:
 
entering child
child's write into pipe, successful
entering parent
parent's read successful, n = 1
 
 parent write to stdout successful
 
What am I doing wrong?  Help!
 
Thanks,
 
-Steve Harris
 
 


The Answer is :

 
  The "else if (pid == 0) /* child */ { /* ... */ }" branch of the code
  needs to call one of the exec* family of functions, preferably before
  doing anything else.
 
  On UNIX systems, the fork function causes a new process to be created,
  but the C run-time library vfork function on OpenVMS does not.  Instead,
  it acts more like setjmp -- when you call vfork it returns zero the first
  time (in the one and only process usually called parent), then the code
  must call one of the exec* family of functions, after which vfork returns
  a second time with a nonzero value in the parent (negative means error,
  positive is the pid of the newly created child process, which doesn't
  exist if an exec* function was not called).
 
  In OpenVMS V7.2, there is a new CRTL function:
 
    decc$set_child_standard_streams()
 
  which makes it a somewhat easier to port Unix applications using a real
  fork() function to VMS.
 
  Attached is the description of this function from DEC C Run-Time Library
  Reference Manual Order Number: AA-PUNEF-TK which is shipped with the
  DEC C V6.0 compiler. There is an example of parent/child communication
  in this description.
 
  decc$set_child_standard_streams() was ECOed to V7.1 and is available in
  the CRTL backport object library. This type of parent/child communication
  works not only for pipes, but for sockets also and, as the example shows,
  for both .C and .COM child.
 
decc$set_child_standard_streams
 
     For a child spawned by a function from the exec family of
     functions, associates specified file descriptors with a child's
     standard streams: stdin, stdout, and stderr.
 
----------------------------------------------------------------------------
 
Format
 
     #include <unistd.h>
 
     int decc$set_child_standard_streams (int fd1, int fd2, int fd3);
 
----------------------------------------------------------------------------
 
Arguments
 
     fd1
 
     The file associated with this file descriptor in the parent
     process is associated with file descriptor number 0 (stdin) in the
     child process. If -1 is specified, the file associated with the
     parent's file descriptor number 0 is used (the default).
 
     fd2
 
     The file associated with this file descriptor in the parent
     process is associated with file descriptor number 1 (stdout) in
     the child process. If -1 is specified, the file associated with
     the parent's file descriptor number 1 is used (the default).
 
     fd3
 
     The file associated with this file descriptor in the parent
     process is associated with file descriptor number 2 (stderr) in
     the child process. If -1 is specified, the file associated with
     the parent's file descriptor number 2 is used (the default).
 
----------------------------------------------------------------------------
 
Description
 
     This function allows mapping of specified file descriptors to the
     child's stdin/out/err streams, thereby compensating, to a certain
     degree, the lack of a real fork function on OpenVMS systems.
 
     On UNIX systems, the code between fork and exec is executed in the
     context of the child process:
 
     parent:
       create pipes p1, p2 and p3
       fork
     child:
       map stdin to p1  like dup2(stdin,  p1);
       map stdout to p2 like dup2(stdout, p2);
       map stderr to p3 like dup2(stderr, p3);
       exec (child reads from stdin and writes to stdout and stderr)
       exit
     parent:
       communicates with the child using pipes
 
     On OpenVMS systems, the same task could be achieved as follows:
 
     parent:
       create pipes p1, p2 and p3
       decc$set_child_standard_streams(p1, p2, p3);
       vfork
       exec (child reads from stdin and writes to stdout and stderr)
     parent:
       communicates with the child using pipes
 
     Once established through the call to
     decc$set_child_standard_streams , the mapping of the child's
     standard streams remains in effect until explicitly disabled by
     one of the following calls:
 
     decc$set_child_standard_streams(-1, -1, -1);
 
        OR
 
     decc$set_child_standard_streams(0, 1, 2);
 
     Usually, the child process inherits all its parent's open file
     descriptors. However, if file descriptor number n was specified in
     the call to decc$set_child_standard_streams , it is not inherited
     by the child process as file descriptor number n; instead, it
     becomes one of the child's standard streams.
 
----------------------------------------------------------------------------
 
Return Value
 
      x    The number of file descriptors set for the child. This number
           does not include file descriptors specified as --1 in the call.
 
      --1  indicates that an invalid file descriptor was specified; errno
           is set to EBADF.
 
----------------------------------------------------------------------------
 
Example
 
 
     parent.c
     ========
 
     #include <stdio.h>
     #include <string.h>
     #include <unistd.h>
 
     int decc$set_child_standard_streams(int, int, int);
 
     main()
     {
         int fdin[2], fdout[2], fderr[2];
         char msg[] = "parent writing to child's stdin";
         char buf[80];
         int nbytes;
 
         pipe(fdin);
         pipe(fdout);
         pipe(fderr);
 
         if ( vfork() == 0 ) {
           decc$set_child_standard_streams(fdin[0], fdout[1], fderr[1]);
           execl( "child", "child" );
         }
         else {
             write(fdin[1], msg, sizeof(msg));
             nbytes = read(fdout[0], buf, sizeof(buf));
             buf[nbytes] = '\0';
             puts(buf);
             nbytes = read(fderr[0], buf, sizeof(buf));
             buf[nbytes] = '\0';
             puts(buf);
         }
     }
 
     child.c
     =======
 
     #include <stdio.h>
     #include <unistd.h>
 
     main()
     {
         char msg[] = "child writing to stderr";
         char buf[80];
         int nbytes;
 
         nbytes = read(0, buf, sizeof(buf));
         write(1, buf, nbytes);
         write(2, msg, sizeof(msg));
     }
 
     child.com
     =========
 
     $ read sys$command s
     $ write sys$output s
     $ write sys$error "child writing to stderr"
 
     This example program returns the following for both child.c and
     child.com:
 
     $ run parent
     parent writing to child's stdin
     child writing to stderr
 
     Note that in order to activate child.com , you must explicitly
     specify execl("child.com", ...) in the parent.c program.
 

answer written or last revised on ( 20-OCT-1998 )

» close window