C语言三进程双管道通信问题:子进程W无输出且未进入第二个fork
问题描述
我正在编写一个使用管道的C语言程序,包含三个进程:父进程P、子进程R、子进程W。P与R通过第一个管道通信,P与W通过第二个管道通信。子进程R读取输入文件,将每行内容通过第一个管道发送给父进程P;父进程P读取内容并判断是否为回文(palindroma),若是则将其发送给子进程W。
代码实现
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/wait.h> #define DIMBUF 1024 int palindroma(const char *stringa) { int len = strlen(stringa); int j = 0; for (int i = len - 1; i >= 0; i--) { if (stringa[i] == stringa[j]) { j++; } else return 0; } return 1; } void child_r(int piper, const char *filename, char tmp[DIMBUF]) { FILE *w_stream, *file; if ((w_stream = fdopen(piper, "w")) == NULL) { perror("writer piper_rp"); exit(1); } printf("Sono il figlio R\n"); if ((file = fopen(filename, "r")) == NULL) { perror("apertura file"); exit(1); } while (fgets(tmp, DIMBUF, file) != NULL) { printf("\tLeggo dal file: %s", tmp); fputs(tmp, w_stream); } fclose(file); fclose(w_stream); } void child_w(int piper, char tmp[DIMBUF]) { FILE *r_stream; if ((r_stream = fdopen(piper, "r")) == NULL) { perror("reader"); exit(1); } while (fgets(tmp, DIMBUF, r_stream) != NULL) { printf("\tMessaggio ricevuto: %s\n", tmp); } fclose(r_stream); exit(0); } void father(int piper_rp, char tmp[DIMBUF], int piper_pw) { FILE *r_stream, *w_stream; int len; if ((r_stream = fdopen(piper_rp, "r")) == NULL) { perror("reader"); exit(1); } if ((w_stream = fdopen(piper_pw, "w")) == NULL) { perror("writer piper_pw"); exit(1); } while (fgets(tmp, DIMBUF, r_stream) != NULL) { len = strlen(tmp); if (tmp[len - 1] == '\n') tmp[len - 1] = 0; int pal = palindroma(tmp); if (pal) { printf("\tMessaggio inviato: %s\n", tmp); fputs(tmp, w_stream); } } fclose(w_stream); fclose(r_stream); } int main(int argc, char const *argv[]) { int piper_rp[2]; int piper_pw[2]; char tmp[DIMBUF]; const char *filename = argv[1]; if (argc != 2) { fprintf(stderr, "Utilizzo %s file.txt \n ", argv[0]); exit(1); } if (pipe(piper_pw) == -1) { perror("errore pipe_pw"); exit(1); } if (pipe(piper_rp) == -1) { perror("errore pipe_rp"); exit(1); } if (fork() == 0) { close(piper_rp[0]); child_r(piper_rp[1], filename, tmp); } else { // padre sleep(1); close(piper_rp[1]); close(piper_pw[0]); printf("\nSono il padre P\n"); father(piper_rp[0], tmp, piper_pw[1]); if (fork() == 0) { printf("\nSono il figlio W\n"); close(piper_pw[1]); child_w(piper_pw[0], tmp); } else wait(NULL); } return 0; }
运行输出
Sono il figlio R Leggo dal file: Ciao Leggo dal file: Mondo Leggo dal file: Ale Leggo dal file: emme Leggo dal file: piip Leggo dal file: veffev Leggo dal file: Scatola Leggo dal file: computer Leggo dal file: america Leggo dal file: abba Leggo dal file: acca Leggo dal file: cielo Leggo dal file: lampada Leggo dal file: effe Sono il padre P Messaggio inviato: emme Messaggio inviato: piip Messaggio inviato: veffev Messaggio inviato: abba Messaggio inviato: acca Messaggio inviato: effe
疑问
为何子进程W没有任何输出?为何程序从未进入第二个fork?
问题分析与修复
核心错误点
父进程提前关闭W管道的读端,导致子进程W无法读取
父进程在fork子进程W前,已经调用close(piper_pw[0]),子进程W继承的该文件描述符已失效,fdopen会失败,无法读取管道内容。父进程先完成所有写入操作再fork子进程W,且写入内容无换行符
父进程执行完father函数后才创建子进程W,此时管道写端已关闭;同时父进程写入管道的回文字符串被去掉了换行符,子进程W用fgets读取时会一直等待换行符或EOF,导致阻塞。进程未正确关闭无关管道端
子进程R未关闭W管道的两端,父进程的管道引用计数异常,可能导致读取进程无法感知EOF。
修复步骤
1. 调整fork子进程W的时机,保留有效管道端
父进程应先创建子进程W,再处理R管道的数据,避免提前关闭W管道的读端。修改main函数的父进程分支:
else { close(piper_rp[1]); // 父进程关闭R管道写端 // 先fork子进程W if (fork() == 0) { close(piper_pw[1]); // 子进程W关闭写端 close(piper_rp[0]); // 子进程W关闭无关的R管道读端 printf("\nSono il figlio W\n"); child_w(piper_pw[0], tmp); } else { close(piper_pw[0]); // 父进程关闭W管道读端 sleep(1); printf("\nSono il padre P\n"); father(piper_rp[0], tmp, piper_pw[1]); fflush(stdout); close(piper_pw[1]); // 父进程写完后关闭写端,触发子进程W的EOF wait(NULL); } }
2. 给写入管道的内容添加换行符
修改father函数的写入逻辑,确保子进程W的fgets能正常读取完整行:
if (pal) { printf("\tMessaggio inviato: %s\n", tmp); fputs(tmp, w_stream); fputs("\n", w_stream); // 添加换行符 }
3. 让子进程R关闭无关管道端
修改子进程R的fork分支,关闭不需要的W管道两端:
if (fork() == 0) { close(piper_rp[0]); close(piper_pw[0]); // 关闭W管道读端 close(piper_pw[1]); // 关闭W管道写端 child_r(piper_rp[1], filename, tmp); }
修复后运行效果
子进程W会正常输出所有回文字符串:
Sono il figlio R Leggo dal file: Ciao Leggo dal file: Mondo Leggo dal file: Ale Leggo dal file: emme Leggo dal file: piip Leggo dal file: veffev Leggo dal file: Scatola Leggo dal file: computer Leggo dal file: america Leggo dal file: abba Leggo dal file: acca Leggo dal file: cielo Leggo dal file: lampada Leggo dal file: effe Sono il padre P Messaggio inviato: emme Messaggio inviato: piip Messaggio inviato: veffev Messaggio inviato: abba Messaggio inviato: acca Messaggio inviato: effe Sono il figlio W Messaggio ricevuto: emme Messaggio ricevuto: piip Messaggio ricevuto: veffev Messaggio ricevuto: abba Messaggio ricevuto: acca Messaggio ricevuto: effe
内容的提问来源于stack exchange,提问作者Valentino Fabris Valenti
相关产品推荐
相关产品推荐

