Pipe, Fork, and Exec - Two Way Communication Between Parent and Child Process
Solution 1
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] = "";
Solution 2
The first entry in your argv2
should be the name of the executable (just like your incoming argv[0]
.
char *argv2[] = {"two_way_pipes", "some random arg...", NULL};
execvp("two_way_pipes", argv2);
From the execvp
man page:
The
execv()
,execvp()
, andexecvpe()
functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer.
Solution 3
As Jonathon Reinhart says you should change this lines:
char *argv2[] = {"some random arg to make sure that argc == 2 in the child", NULL};
execvp("two_way_pipes", argv2);
to:
char *argv2[] = {argv[0], "some random arg...", NULL};
execvp(argv[0], argv2);
then it works as expected:
echo test | ./two_way_pipes
in parent | message received: test
In your program you wrote "two_way_pipes" but it is not in your PATH so you really need the extra ./ so argv[0] then is ("./two_way_pipes").
Anthony Jack
Updated on June 04, 2022Comments
-
Anthony Jack almost 2 years
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
- writing to exec'd child from parent
- reading from parent in exec'd child
- writing back to parent from exec'd child
- reading from exec'd child in parent
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.
-
Anthony Jack over 10 yearsThanks Jonathan. I modified argv2 based on your answer but the parent still isn't getting the data from the exec'd child.
-
Seng Cheong over 10 yearsIt's really not a big deal, as it happens all the time - But it's impressive how often people will misspell someone's name - even when it is a couple hundred pixels away on the same page.
-
Anthony Jack over 10 yearsSorry about that. I suspect that if I were named Anthany people would often misspell it too :-)