An assignment in my Operating Systems class requires me to build a binary process tree by recursively calling exec on the same program. The goal is to split some arbitrary task into separate processes. The parent should communicate with the children, and the children with the parent only via unnamed pipes. The idea is that the parent sends each child half of the work and this continues recursively until a base case is met where the length of the string being passed to each child is <= 2. The child then processes this data and sends the results back to the parent via pipes.
To get a better understanding of how two way communication works with pipes in c I created the following simple program before moving on to the actual assignment. The parent never reads the data from the child process though. I'm expecting the output...
in parent | message received: test
Instead, when I print I get...
in parent | message received:
It seems that buff is empty and not reading from the child process. Can someone please explain what I'm doing wrong and/or the standard way of
I am required to use exec(), pipe(), fork(). Thank you.
/**
* *********************************
* two_way_pipes.c
* *********************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define PARENT_READ read_pipe[0]
#define PARENT_WRITE write_pipe[1]
#define CHILD_WRITE read_pipe[1]
#define CHILD_READ write_pipe[0]
#define DEBUGGING 1
int main(int argc, char **argv) {
char buff[5];
// in the child process that was exec'd on the orginal call to two_way_pipes
if(argc == 2) {
read(STDIN_FILENO, buff, 4); // this should read "test" from stdin
buff[4] = '\0';
fprintf(stdout, "%s\n", buff); // this should right "test" to stdout and be read by the parent process
// int the root process, the original call to two_way_pipes with no args
} else {
int pid;
int read_pipe[2];
int write_pipe[2];
pipe(read_pipe);
pipe(write_pipe);
pid = fork();
// parent process
if(pid > 0) {
close(CHILD_READ);
close(CHILD_WRITE);
write(PARENT_WRITE, "test", 4); // attempting to write this to the child
struct timeval tv;
fd_set readfds;
tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(PARENT_READ, &readfds);
select(PARENT_READ + 1, &readfds, NULL, NULL, &tv);
if(FD_ISSET(PARENT_READ, &readfds)) {
read(PARENT_READ, buff, 4); // should read "test" which was written by the child to stdout
buff[4] = '\0';
close(PARENT_READ);
close(PARENT_WRITE);
fprintf(stderr, "in parent | message received: %s\n", buff); // "test" is not in buff
}
// child process
} else if(pid == 0) {
close(PARENT_READ);
close(PARENT_WRITE);
dup2(CHILD_READ, STDIN_FILENO);
dup2(CHILD_WRITE, STDOUT_FILENO);
close(CHILD_READ);
close(CHILD_WRITE);
char *argv2[] = {"some random arg to make sure that argc == 2 in the child", NULL};
execvp("two_way_pipes", argv2);
_exit(0);
// error forking child process
} else {
fprintf(stderr, "error forking the child\n");
}
}
}
Update
Based on Jonathon's answer I modified the arg2 array being passed into execvp to...
char *argv2[] = {"two_way_pipes", "1", NULL};
execvp("two_way_pipes", argv2);
This didn't fix the issue. The parent still wasn't able to read "test" back from the client. However, in response to Jonathon's answer and William's comment I started tweaking my exec call and for some reason changing it to the line show below worked.
execl("two_way_pipes", "two_way_pipes", "1", NULL);
I'll gladly accept any answers explaining why the execvp call wouldn't work but the execl call did.
Besides the issue mentioned by Jonathon Reinhart, most probably the call to execv()
fails.
To test this modify these lines
execvp("two_way_pipes", argv2);
_exit(0);
to be
...
#include <errno.h>
...
execvp("two_way_pipes", argv2); /* On sucess exec*() functions never return. */
perror("execvp() failed); /* Getting here means execvp() failed. */
_exit(errno);
Expect to receive
execvp() failed: No such file or directory
To fix this change
execvp("two_way_pipes", argv2);
to be
execvp("./two_way_pipes", argv2);
Also if the child was not exec*()
ed then this line
read(PARENT_READ, buff, 4); // should read "test" which was written by the child to stdout
fails and in turn buff
is not initialised and therefore this line
fprintf(stderr, "in parent | message received: %s\n", buff);
provokes undefined behaviour.
To fix this at least properly initialise buff
by changing
char buff[5];
to be
char buff[5] = "";
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With