从管道执行读写时出现死锁?
Deadlock when performing reading and writing from pipe?
我想通过 dup2() 和 pipe() 使用 I/O 重定向创建管道,以便将每个子进程的输出作为输入传递到下一个子进程。但是,我注意到即使在关闭父进程和子进程中的所有管道之后,子进程也不会终止(我怀疑这是 grep and/or cat 执行读写时的死锁)。
我还注意到,如果我尝试执行一个管道,其中每个子进程都不依赖于前一个子进程的输出(例如 echo "hi" | echo "hello" | echo "123"),那么子进程成功退出。这似乎也表明在调用 execvp() 后管道 reading/writing 时存在死锁。
我对问题的理解是否正确,如何修复程序使其具有预期的行为?
我写了一个较小的程序来演示上面讨论的控制流程。
#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;
}
我建议您按照这些思路将问题最小化并确认它没有挂起。看起来是缺少的 close(rd)
让您感到困惑:
#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;
}
下一步将是删除重复数据,例如,通过对共享部分使用函数。
我想通过 dup2() 和 pipe() 使用 I/O 重定向创建管道,以便将每个子进程的输出作为输入传递到下一个子进程。但是,我注意到即使在关闭父进程和子进程中的所有管道之后,子进程也不会终止(我怀疑这是 grep and/or cat 执行读写时的死锁)。
我还注意到,如果我尝试执行一个管道,其中每个子进程都不依赖于前一个子进程的输出(例如 echo "hi" | echo "hello" | echo "123"),那么子进程成功退出。这似乎也表明在调用 execvp() 后管道 reading/writing 时存在死锁。
我对问题的理解是否正确,如何修复程序使其具有预期的行为?
我写了一个较小的程序来演示上面讨论的控制流程。
#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;
}
我建议您按照这些思路将问题最小化并确认它没有挂起。看起来是缺少的 close(rd)
让您感到困惑:
#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;
}
下一步将是删除重复数据,例如,通过对共享部分使用函数。