HP OpenVMS Systemsask the wizard |
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.
|