Given that the processing is simply a question of one message from parent to child (which must be complete before the child responds), and one message from child to parent, then it is easy enough to handle:
- Create two pipes, one for communication to child, one for communication to parent.
- Fork.
- Child process duplicates the relevant ends of the pipes (read end of 'to-child' pipe, write end of 'to-parent' pipe) to standard input, output.
- Child closes all pipe file descriptors.
- Child execs test program (or prints a message to standard error reporting failure and exits).
- Parent closes the irrelevant ends of the pipes.
- Parent writes the message to the child and closes the pipe.
- Parent reads the response from the child and closes the pipe.
- Parent continues on its merry way.
This leaves the child process lying around as a zombie. If the parent is going to do this more than once, or just needs to know the exit status of the child, then after closing the read pipe, it will wait for the child to die, collecting its status.
All this is straight-forward, routine coding. I'm sure you could find examples on SO.
Since apparently there are no suitable examples on Stack Overflow, here is a simple implementation of the code outlined above. There are two source files, basic_pipe.c
for the basic piping work, and myprogram.c
which is supposed to respond to the prompts shown in the question. The first is almost general purpose; it should probably loop on the read operation (but that hasn't mattered on the machine I tested it on, which is running an Ubuntu 14.04 derivative). The second is very specialized.
System calls
basic_pipe.c
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
static char msg_for_child[] = "calculate 2 + 2\nsay 'hello world!'\n";
static char cmd_for_child[] = "./myprogram";
static void err_syserr(const char *fmt, ...);
static void be_childish(int to_child[2], int fr_child[2]);
static void be_parental(int to_child[2], int fr_child[2], int pid);
int main(void)
{
int to_child[2];
int fr_child[2];
if (pipe(to_child) != 0 || pipe(fr_child) != 0)
err_syserr("Failed to open pipes\n");
assert(to_child[0] > STDERR_FILENO && to_child[1] > STDERR_FILENO &&
fr_child[0] > STDERR_FILENO && fr_child[1] > STDERR_FILENO);
int pid;
if ((pid = fork()) < 0)
err_syserr("Failed to fork\n");
if (pid == 0)
be_childish(to_child, fr_child);
else
be_parental(to_child, fr_child, pid);
printf("Process %d continues and exits\n", (int)getpid());
return 0;
}
static void be_childish(int to_child[2], int fr_child[2])
{
printf("Child PID: %d\n", (int)getpid());
fflush(0);
if (dup2(to_child[0], STDIN_FILENO) < 0 ||
dup2(fr_child[1], STDOUT_FILENO) < 0)
err_syserr("Failed to set standard I/O in child\n");
close(to_child[0]);
close(to_child[1]);
close(fr_child[0]);
close(fr_child[1]);
char *args[] = { cmd_for_child, 0 };
execv(args[0], args);
err_syserr("Failed to execute %s", args[0]);
/* NOTREACHED */
}
static void be_parental(int to_child[2], int fr_child[2], int pid)
{
printf("Parent PID: %d\n", (int)getpid());
close(to_child[0]);
close(fr_child[1]);
int o_len = sizeof(msg_for_child) - 1; // Don't send null byte
if (write(to_child[1], msg_for_child, o_len) != o_len)
err_syserr("Failed to write complete message to child\n");
close(to_child[1]);
char buffer[4096];
int nbytes;
if ((nbytes = read(fr_child[0], buffer, sizeof(buffer))) <= 0)
err_syserr("Failed to read message from child\n");
close(fr_child[0]);
printf("Read: [[%.*s]]\n", nbytes, buffer);
int corpse;
int status;
while ((corpse = waitpid(pid, &status, 0)) != pid && corpse != -1)
err_syserr("Got pid %d (status 0x%.4X) instead of pid %d\n",
corpse, status, pid);
printf("PID %d exited with status 0x%.4X\n", pid, status);
}
static void err_syserr(const char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
exit(EXIT_FAILURE);
}
myprogram.c
#include <stdio.h>
int main(void)
{
char buffer[4096];
char *response[] =
{
"4",
"hello world!",
};
enum { N_RESPONSES = sizeof(response)/sizeof(response[0]) };
for (int line = 0; fgets(buffer, sizeof(buffer), stdin) != 0; line++)
{
fprintf(stderr, "Read line %d: %s", line + 1, buffer);
if (line < N_RESPONSES)
{
printf("%s\n", response[line]);
fprintf(stderr, "Sent line %d: %s\n", line + 1, response[line]);
}
}
fprintf(stderr, "All done\n");
return 0;
}
Example output
Note that there is no guarantee that the child will complete before the parent starts executing the be_parental()
function.
Child PID: 19538
Read line 1: calculate 2 + 2
Sent line 1: 4
Read line 2: say 'hello world!'
Sent line 2: hello world!
All done
Parent PID: 19536
Read: [[4
hello world!
]]
PID 19538 exited with status 0x0000
Process 19536 continues and exits