I have a small program in which there're a parent process and two child processes. First, the parent process sends data through pipe to its children (childA, childB). To make it clearer, I have a pointers array which has elements pointing to a struct (Rabbit). The parent process filters this array and sends the filtered data to childA and the same for childB except this data will differ. Then childA and childB does something with its data and send it back to the parent process. I have to implement it with pipes. What's wrong with this code snippet and how can I solve this problem?
int pipefd_a[2];
int pipefd_b[2];
pid_t child_a, child_b;
if (pipe(pipefd_a) == -1) {
perror("Error during opening pipe A!");
exit(EXIT_FAILURE);
}
if (pipe(pipefd_b) == -1) {
perror("Error during opening pipe B!");
exit(EXIT_FAILURE);
}
child_a = fork();
if (child_a == 0) { // Child A
close(pipefd_a[1]);
Rabbit rabbit;
while (read(pipefd_a[0], &rabbit, sizeof(Rabbit))) {
// Do something
}
close(pipefd_a[0]);
} else {
child_b = fork();
if (child_b == 0) { // Child B
close(pipefd_b[1]);
Rabbit rabbit;
while (read(pipefd_b[0], &rabbit, sizeof(Rabbit))) {
// Do something
}
close(pipefd_b[0]);
} else {
Rabbit** rabbits_a = (Rabbit**)malloc(sizeof(Rabbit*) * size);
...
// Filter, count_a will be the size of the filtered array
close(pipefd_a[0]);
for (unsigned i = 0; i < count_a; i) {
Rabbit rabbit = {rabbits_a[i]->name, rabbits_a[i]->district, rabbits_a[i]->part_count};
write(pipefd_a[1], &rabbit, sizeof(Rabbit));
}
...
// The same for B
...
fflush(NULL);
wait(NULL);
}
}
CodePudding user response:
At a quick glance, it looks like you're on the right track, but you're missing some code in the child processes to write the processed data back to the pipe so it can be picked up by the parent process. Furthermore, your wait()
call should probably be the very first call in the parent process after your calls to fork()
, because you have no guarantee that the child processes will run before the parent process. Also, you might want to set up some sort of loop structure so that you wait
until both child processes terminate prior to trying to read from their pipes.
So, your program structure might look like this:
// #include <sys/stat.h>
#include <unistd.h>
// ...
// ... assuming there's code you didn't show that writes the initial data to the pipes to get it into the children processes...
child_a = fork();
if (child_a == 0) {
// Read from pipe, do stuff, and write to pipefd_a[1] (so you can't close it)
} else {
child_b = fork();
if (child_b == 0) {
// read from pipe, do stuff, and write to pipefd_b[1]
} else {
// Wait for both child processes to end
pid_t pids[] = {child_a, child_b};
pid_t returned_pid;
int i;
for (i = 0; i < 2; i ) {
// the wait() call will return the pid of the process that
// caused wait() to terminate. Since you forked off two processes
// wait could either return child_a or child_b, and we
// want to make sure both processes are finished prior to
// trying to read stuff from their pipes, hence the outer
// for loop
// Update: I realized that if the process is already dead,
// then the do-while loop just runs forever, so I'm throwing in a check
// to make sure the process is alive before starting the loop
struct stat dummy;
char buf[70];
// Every running process on a UNIX system has a corresponding
// ID number and a corresponding subdirectory within the /proc
// directory, so if the directory doesn't exist, the process doesn't exist either
// sprintf(buf, "/proc/%d", pids[i]);
// ---------------------------------------
// Edited again because I'm dumb and forgot
// /proc doesn't exist on macOS (which is what I'm on).
// If you're there, then you can do something like this, which I'll explain below.
sprintf(buf, "ps -e | awk '{ print $1 }' | grep %d", pids[i]);
int result = system(buf);
// If on Linux you can do...
/*
if ( stat(buf, &dummy) < 0 ) {
// Process is dead, continue
continue;
} */
// If on a UNIX system (mac or Linux)...
if (result != 0) {
// Process is dead, continue
continue;
}
do {
returned_pid = wait(NULL);
} while (returned_pid != pids[i]);
}
// At this point, both child processes have terminated, so NOW
// we can read from the pipes
// Read from pipefd_a[0]
// Read from pipefd_b[0]
}
}
So, what the heck is this string: `ps -e | awk '{ print $1 }' | grep %d`
This is a string of three shell commands:
ps -e
- this will print information about all currently running processes. This includes the process IDsawk '{ print $1 }'
- this runs an awk script that prints only the first field in a given string, where fields are delimited by whitespacegrep %d
- this will search the given file (stdin in this case) for the PID provided (because this is in a format string,%d
gets replaced with a process ID)
Then, the system
command runs entire string. A return value of 0 means that the command string was executed successfully, meaning that the process ID was found in the list of currently running processes.
Anyway, I hope you're able to get some valuable info from this post.