I want to create a pipeline using I/O redirection via dup2() and pipe() in order to pass the output of each subprocess as the input into the next subprocess. However, I'm noticing that even after I close all the pipes BOTH in the parent and child processes, the children do not terminate (which I suspect to be a deadlock when grep and/or cat performs reading and writing).
I also noticed that if I try to execute a pipeline where each subprocess is not dependent on the output of the previous subprocess (e.g. echo "hi" | echo "hello" | echo "123"), then the children exit successfully. This also seems to suggests that there is a deadlock when reading/writing from pipes after execvp() is called.
Am I correct in understanding the problem, and how can I fix the program to have its intended behavior?
I wrote a smaller program to demonstrate the control flow of what I discussed above.
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
int main() {
int fd[2][2];
for(int i = 0; i < 2; i ) {
pipe(fd[i]);
}
for(int i = 0; i < 3; i ) {
int child_pid = fork();
if(child_pid == 0) {
if(i == 0) {
dup2(fd[0][1], STDOUT_FILENO);
for(int i = 0; i < 2; i ) {
close(fd[i][0]);
close(fd[i][1]);
}
char* argv[] = {"cat", "/usr/share/dict/words", NULL};
execvp(argv[0], argv);
}
else if(i == 1) {
dup2(fd[0][0], STDIN_FILENO);
dup2(fd[1][1], STDOUT_FILENO);
for(int i = 0; i < 2; i ) {
close(fd[i][0]);
close(fd[i][1]);
}
char* argv[] = {"grep", "yum", NULL};
execvp(argv[0], argv);
}
else {
dup2(fd[1][0], STDIN_FILENO);
int rd = open("test.txt", O_RDWR | O_CREAT, 0777);
dup2(rd, STDOUT_FILENO);
for(int i = 0; i < 2; i ) {
close(fd[i][0]);
close(fd[i][1]);
}
char* argv[] = {"grep", "yummi", NULL};
execvp(argv[0], argv);
}
}
}
for(int i = 0; i < 2; i ) {
close(fd[i][0]);
close(fd[i][1]);
}
return 0;
}
CodePudding user response:
I suggest you minimize the problem along these lines and verify that it doesn't hang. It looks it's the missing close(rd)
that is tripping you up:
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
int fd[2];
if(pipe(fd)) {
printf("pipe failed\n");
return 1;
}
pid_t pid = fork();
if(pid == -1) {
printf("fork failed\n");
return 1;
}
if(!pid) {
//printf("child\n");
if(dup2(fd[1], STDOUT_FILENO) == -1) {
printf("dup2 failed\n");
return 1;
}
close(fd[0]);
close(fd[1]);
char *argv[] = {"cat", "/usr/share/dict/words", NULL};
if(execvp(argv[0], argv) == -1) {
printf("execvp failed\n");
return 1;
}
}
pid_t pid2 = fork();
if(pid == -1) {
printf("fork2 failed\n");
return 1;
}
if(!pid2) {
// printf("child2\n");
if(dup2(fd[0], STDIN_FILENO) == -1) {
printf("dup2 failed\n");
return 1;
}
close(fd[0]);
close(fd[1]);
int rd = open("test.txt", O_RDWR | O_CREAT, 0777);
if(dup2(rd, STDOUT_FILENO) == -1) {
printf("dup2 failed\n");
return 1;
}
close(rd);
char *argv[] = {"grep", "yum", NULL};
if(execvp(argv[0], argv) == -1) {
printf("execvp failed\n");
return 1;
}
}
// printf("parent\n");
close(fd[0]);
close(fd[1]);
int status;
waitpid(pid, &status, 0);
waitpid(pid2, &status, 0);
return 0;
}
The next step would be to deduplicate this, say, by using functions for the shared parts.