C语言多进程管道通信:无法持续读取数据的问题求助
多进程管道通信循环退出问题的修复
问题概述
我编写了一个C语言程序,创建两个子进程分别实现字典序比较(lexcmp)和长度比较(lencmp)功能,通过4个管道实现父进程与子进程的通信。程序设计为循环持续处理用户输入,但当父进程尝试从管道读取子进程的计算结果时,会直接退出该循环。预期效果是向管道写入字符串后,读取子进程返回的结果并打印。
关键错误分析
- 管道过早关闭:父进程在第一次处理请求时就关闭了管道的写端(如
fd1[1])和读端(如fd2[0]),导致后续循环无法再使用这些管道通信。同时子进程会因为管道写端关闭收到EOF,退出自身循环,父进程后续读取时也会得到EOF,触发mygets返回NULL退出主循环。 - 函数指针赋值逻辑错误:
loopcmp中判断子进程类型时,if (strcmp(argv[1], "lexcmp"))的逻辑是字符串不相等时才赋值lexcmp函数,与预期完全相反,应使用!strcmp判断相等。 - 输入处理残留问题:
scanf("%d", &index)后未清理输入缓冲区的换行符,导致下一次mygets读取空行,直接触发退出逻辑。 - 结果缓冲区不当修改:父进程中
buf[4] = '\n';会破坏读取到的结果,应根据实际读取长度处理字符串终止。
修复后的代码
父进程代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <sys/wait.h> #define LINELEN (80) char *mygets(char *buf, int len); void clear_input_buffer(); int main() { // fd[0] - 读端, fd[1] - 写端 char *cmpstr[] = {"lexcmp", "lencmp"}; int veclen = sizeof(cmpstr) / sizeof(char *); char str1[LINELEN + 1]; char str2[LINELEN + 1]; int index; int fd1[2], fd2[2], fd3[2], fd4[2]; if (pipe(fd1) == -1 || pipe(fd2) == -1 || pipe(fd3) == -1 || pipe(fd4) == -1) { perror("pipe"); return -2; } // 创建lexcmp子进程 switch (fork()) { case -1: perror("fork"); return -2; case 0: close(fd3[1]); close(fd3[0]); close(fd4[1]); close(fd4[0]); close(fd1[1]); close(fd2[0]); dup2(fd1[0], STDIN_FILENO); close(fd1[0]); dup2(fd2[1], STDOUT_FILENO); close(fd2[1]); execlp("./loopcmp", "lexcmp", NULL); perror("execlp"); return -2; default: break; } // 创建lencmp子进程 switch (fork()) { case -1: perror("fork"); return -2; case 0: close(fd1[1]); close(fd1[0]); close(fd2[1]); close(fd2[0]); close(fd3[1]); close(fd4[0]); dup2(fd3[0], STDIN_FILENO); close(fd3[0]); dup2(fd4[1], STDOUT_FILENO); close(fd4[1]); execlp("./loopcmp", "lencmp", NULL); perror("execlp"); return -2; default: break; } // 父进程关闭不需要的管道端 close(fd1[0]); close(fd2[1]); close(fd3[0]); close(fd4[1]); while (1) { printf("Please enter first string:\n"); if (mygets(str1, LINELEN) == NULL) break; printf("Please enter second string:\n"); if (mygets(str2, LINELEN) == NULL) break; do { printf("Please choose:\n"); for (int i = 0; i < veclen; i++) printf("%d - %s\n", i, cmpstr[i]); scanf("%d", &index); clear_input_buffer(); // 清理输入缓冲区的换行符 } while ((index < 0) || (index >= 2)); if (index == 0) // 处理lexcmp请求 { printf("you are in lexcmp\n"); if (write(fd1[1], str1, strlen(str1) + 1) == -1 || write(fd1[1], str2, strlen(str2) + 1) == -1) { perror("write to lexcmp pipe"); break; } char buf[80]; ssize_t read_len = read(fd2[0], buf, sizeof(buf)-1); if (read_len <= 0) { perror("read from lexcmp child"); break; } buf[read_len] = '\0'; printf("%s(%s, %s) == %s", cmpstr[index], str1, str2, buf); } if (index == 1) // 处理lencmp请求 { printf("you are in lencmp\n"); if (write(fd3[1], str1, strlen(str1) + 1) == -1 || write(fd3[1], str2, strlen(str2) + 1) == -1) { perror("write to lencmp pipe"); break; } char buf[80]; ssize_t read_len = read(fd4[0], buf, sizeof(buf)-1); if (read_len <= 0) { perror("read from lencmp child"); break; } buf[read_len] = '\0'; printf("%s(%s, %s) == %s", cmpstr[index], str1, str2, buf); } } // 退出前关闭管道,通知子进程结束 close(fd1[1]); close(fd2[0]); close(fd3[1]); close(fd4[0]); // 等待子进程退出,避免僵尸进程 wait(NULL); wait(NULL); return 0; } char *mygets(char *buf, int len) { char *retval = fgets(buf, len, stdin); if (!retval) return NULL; buf[len-1] = '\0'; size_t buf_len = strlen(buf); if (buf_len > 0 && buf[buf_len - 1] == '\n') buf[buf_len - 1] = '\0'; else { // 清空超长输入的剩余字符 int ch; while ((ch = getchar()) != '\n' && ch != EOF); } return retval; } void clear_input_buffer() { int ch; while ((ch = getchar()) != '\n' && ch != EOF); }
loopcmp 代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #define LINELEN (80) int lencmp(const char *str1, const char *str2); int lexcmp(const char *str1, const char *str2); char *mygets(char *buf, int len); int main(int argc, char *argv[]) { int(*cmpfunc)(const char *, const char *) = NULL; char str1[LINELEN + 1]; char str2[LINELEN + 1]; if (argc != 2) return -1; // 修正函数指针赋值逻辑 if (!strcmp(argv[1], "lexcmp")) cmpfunc = lexcmp; else if (!strcmp(argv[1], "lencmp")) cmpfunc = lencmp; else return -1; while (1) { if (mygets(str1, LINELEN) == NULL) break; if (mygets(str2, LINELEN) == NULL) break; printf("%d\n", cmpfunc(str1, str2)); fflush(stdout); } return 0; } int lencmp(const char *str1, const char *str2) { int val = strlen(str1) - strlen(str2); if (val < 0) return 1; if (val > 0) return 2; return 0; } int lexcmp(const char *str1, const char *str2) { int val = strcmp(str1, str2); if (val < 0) return 1; if (val > 0) return 2; return 0; } char *mygets(char *buf, int len) { char *retval = fgets(buf, len, stdin); if (!retval) return NULL; buf[len-1] = '\0'; size_t buf_len = strlen(buf); if (buf_len > 0 && buf[buf_len - 1] == '\n') buf[buf_len - 1] = '\0'; else { int ch; while ((ch = getchar()) != '\n' && ch != EOF); } return retval; }
编译与运行说明
- 先编译
loopcmp生成可执行文件:gcc loopcmp.c -o loopcmp - 编译父进程代码:
gcc parent.c -o parent - 运行父进程:
./parent
内容的提问来源于stack exchange,提问作者Yam Horin
相关产品推荐
相关产品推荐

