What I am trying to achieve is, creating 2 child processes say "left" and "right", left child executes a process called ./left that gets 2 inputs from stdin and sums it, then forward it to its parent process by stdout. Then right child again executes a process that again sums the 2 stdin inputs and forwards it to its parent. In code below, left result successfully handled and printed via stderr but right result is a garbage value, I couldnt figure out why.
main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
//###### 0 is read end, 1 is write end
int p2c_l[2]; // Parent to child left pipe, p2c[0] is read end, p2c[1] is write end from parent perspective
int c2p_l[2]; // Child to parent left pipe
// Create pipes
pipe(p2c_l);
pipe(c2p_l);
int pid = fork();
if (pid == 0) { //Left child
fprintf(stderr, "Left child process\n");
// close right child pipes
close(c2p_l[0]);
close(p2c_l[1]);
dup2(p2c_l[0], 0);
dup2(c2p_l[1], 1);
char* args[] = {"./left", NULL};
execvp(args[0], args);
fprintf(stderr, "Left child process failed\n");
exit(EXIT_FAILURE);
} else { // Parent process
close(c2p_l[1]);
close(p2c_l[0]);
// dup2(p2c_l[1], 1);
// dup2(c2p_l[0], 0);
fprintf(stderr, "Parent process after first fork\n");
int n1, n2;
n1 = 5;
n2 = 10;
write(p2c_l[1], &n1, sizeof(n1));
write(p2c_l[1], &n2, sizeof(n2));
int resultFromLeftChild;
read(c2p_l[0], &resultFromLeftChild, sizeof(resultFromLeftChild));
fprintf(stderr, "Left child result: %d\n", resultFromLeftChild);
int p2c_r[2]; // Parent to child right pipe
int c2p_r[2]; // Child to parent right pipe
int pid2 = fork();
if (pid2 == 0) { //Right child
fprintf(stderr, "Right child process\n");
// close left child pipes
close(p2c_r[0]);
close(c2p_r[1]);
//make pc2_r[0] and c2p_r[1] as stdin and stdout
dup2(p2c_r[0], 0);
dup2(c2p_r[1], 1);
char* args[] = {"./right", NULL};
execvp(args[0], args);
fprintf(stderr, "Right child process failed\n");
exit(EXIT_FAILURE);
} else { // Parent process
close(p2c_r[1]);
close(c2p_r[0]);
// dup2(p2c_r[1], 1);
// dup2(c2p_r[0], 0);
fprintf(stderr, "Parent process after second fork\n");
int n1, n2;
n1 = resultFromLeftChild;
n2 = 10;
// printf("%d\n", n1);
// printf("%d\n", n2);
write(p2c_r[1], &n1, sizeof(n1));
write(p2c_r[1], &n2, sizeof(n2));
int resultFromRightChild;
read(c2p_r[0], &resultFromRightChild, sizeof(resultFromRightChild));
fprintf(stderr, "Right child result: %d\n", resultFromRightChild);
int result = resultFromLeftChild + resultFromRightChild;
fprintf(stderr, "Result: %d\n", result);
}
}
return 0;
}
left.c and right.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int num1, num2;
if (argc != 1) {
printf("Usage: %s \n", argv[0]);
return 1; // Error code for incorrect usage
}
scanf("%d", &num1);
scanf("%d", &num2);
// Calculate the addition
int result = num1 + num2;
//printf("Inputs: %d %d \n", num1, num2);
// Print the result
printf("%d\n", result);
return 0; // Successful execution
}
I expected to get Right child result: 25 as output but i get
Right child result: {garbage value} as output.
One part of the problem is that you're not closing enough pipe descriptors.
Rule of thumb: If you
dup2()one end of a pipe to standard input or standard output, close both of the original file descriptors frompipe()as soon as possible. In particular, that means before using any of theexec*()family of functions. The rule also applies with eitherdup()orfcntl()withF_DUPFD.Another problem in the current revision of the code (revision 4) is that the children expect to read ASCII numbers using
scanf()but the parent sends binary numbers.The biggest problem is that you don't create the pipes for the child process. A secondary problem is that you close the wrong ends of the right-child pipes in the parent process.
Fixing these issues leads to code like this:
Sample output:
Note that this code is still a bit sloppy about checking that read and write operations succeeded. It's a good idea, especially when things aren't working yet, to systematically check every operation that can fail and report failures. That means checking calls that open files or create/modify file descriptors, the read and write operations, and so on. The data read off the pipe is not a null-terminated string; the code should fix things up so that it does null-terminate the data off the pipe.
A more thorough reworking of the code would put the code for each child into a function (a common function with arguments to identify what's to happen) that is called twice with appropriate arguments. That code could look like this (83 lines vs 139 in prior version):
Sample output:
The error checking still needs fixing.