两个C程序管道双向通信问题:接收返回值始终为0的排查
问题:父子进程双向管道通信异常,父进程读取返回值为0
需求是编写两个C程序ex1.c和ex2.c:
ex1通过fork创建子进程并执行ex2ex1向ex2发送2个字符串和1个整数ex2向ex1返回一个整数
编译运行./ex1后,发现ex1读取到的返回整数始终为0,以下是原代码及解决方案:
原代码
ex1.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main() { int pipefd[2]; if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } int integerData = 42; char string1[] = "Hello good"; char string2[] = "World "; pid_t pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } else if (pid == 0) { // Child process (loopcmp.c) close(pipefd[1]); // Close the write end of the pipe // Redirect stdin to read from the pipe if (dup2(pipefd[0], STDIN_FILENO) == -1) { perror("dup2"); exit(EXIT_FAILURE); } // Execute ex1.c execlp("./ex1", "ex1", NULL); // If execlp returns, an error occurred perror("execlp"); exit(EXIT_FAILURE); } else { // Parent process (nicecmp.c) close(pipefd[0]); // Close the read end of the pipe // Serialize the data as a single string char buffer[256]; snprintf(buffer, sizeof(buffer), "%s:%s:%d", string1, string2, integerData); // Write the serialized string to the pipe if (write(pipefd[1], buffer, strlen(buffer)) == -1) { perror("write"); exit(EXIT_FAILURE); } close(pipefd[1]); // Close the write end of the pipe // Wait for the child process to finish int status; wait(&status); int num; read(pipefd[0], &num, sizeof(int)); close(pipefd[0]); printf("the num from loopcmp: %d\n",num); } return 0; }
ex2.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main() { char buffer[256]; // Read the serialized string from stdin (the pipe) if (read(STDIN_FILENO, buffer, sizeof(buffer)) == -1) { perror("read"); exit(EXIT_FAILURE); } // Parse the data from the serialized string char string1[100]; char string2[100]; int integerData; sscanf(buffer, "%[^:]:%[^:]:%d", string1, string2, &integerData); // Now you have the two strings and the integer data from nicecmp.c printf("Received data from nicecmp.c:\n"); printf("String 1: %s\n", string1); printf("String 2: %s\n", string2); printf("Integer: %d\n", integerData); int num = 556; write(STDOUT_FILENO, &num, sizeof(int)); return 0; }
问题分析
- 子进程执行目标错误:原代码中子进程执行的是
./ex1而非./ex2,导致逻辑完全混乱 - 单管道无法实现双向通信:管道是半双工的,单个管道只能单向传输数据;且父进程提前关闭了管道读端,后续尝试读取已关闭的描述符必然失败
- 子进程输出未定向到父进程可读管道:
ex2向STDOUT_FILENO写入的数据没有被引导到ex1的读取管道,父进程无法获取返回值 - 读取时机错误:父进程在
wait子进程退出后才尝试读取,此时子进程的输出资源已被释放,无法读取有效数据
修正后的代码
ex1.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main() { int pipe_parent_to_child[2]; // 父进程→子进程的通信管道 int pipe_child_to_parent[2]; // 子进程→父进程的通信管道 // 创建两个双向管道 if (pipe(pipe_parent_to_child) == -1 || pipe(pipe_child_to_parent) == -1) { perror("pipe"); exit(EXIT_FAILURE); } int integerData = 42; char string1[] = "Hello good"; char string2[] = "World "; pid_t pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程:执行ex2 close(pipe_parent_to_child[1]); // 关闭父→子管道的写端 close(pipe_child_to_parent[0]); // 关闭子→父管道的读端 // 重定向stdin到父→子管道的读端 if (dup2(pipe_parent_to_child[0], STDIN_FILENO) == -1) { perror("dup2 stdin"); exit(EXIT_FAILURE); } // 重定向stdout到子→父管道的写端 if (dup2(pipe_child_to_parent[1], STDOUT_FILENO) == -1) { perror("dup2 stdout"); exit(EXIT_FAILURE); } // 关闭已重定向的原始管道描述符 close(pipe_parent_to_child[0]); close(pipe_child_to_parent[1]); // 执行目标程序ex2 execlp("./ex2", "ex2", NULL); // execlp返回则说明执行失败 perror("execlp"); exit(EXIT_FAILURE); } else { // 父进程 close(pipe_parent_to_child[0]); // 关闭父→子管道的读端 close(pipe_child_to_parent[1]); // 关闭子→父管道的写端 // 序列化数据为带终止符的字符串 char buffer[256]; snprintf(buffer, sizeof(buffer), "%s:%s:%d", string1, string2, integerData); // 写入数据到子进程,包含字符串终止符确保解析正确 ssize_t bytes_written = write(pipe_parent_to_child[1], buffer, strlen(buffer) + 1); if (bytes_written == -1) { perror("write to child"); exit(EXIT_FAILURE); } // 关闭写端,告知子进程数据发送完毕 close(pipe_parent_to_child[1]); // 读取子进程返回的整数 int num; ssize_t bytes_read = read(pipe_child_to_parent[0], &num, sizeof(int)); if (bytes_read == -1) { perror("read from child"); exit(EXIT_FAILURE); } else if (bytes_read != sizeof(int)) { fprintf(stderr, "读取子进程数据不完整\n"); exit(EXIT_FAILURE); } // 关闭读端 close(pipe_child_to_parent[0]); // 等待子进程退出并检查状态 int status; wait(&status); if (WIFEXITED(status)) { printf("子进程正常退出,退出码:%d\n", WEXITSTATUS(status)); } printf("从ex2收到的整数:%d\n", num); } return 0; }
ex2.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main() { char buffer[256]; // 从stdin读取父进程发送的数据 ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1); if (bytes_read == -1) { perror("read from parent"); exit(EXIT_FAILURE); } // 手动添加字符串终止符,确保sscanf解析正确 buffer[bytes_read] = '\0'; // 解析字符串中的数据 char string1[100]; char string2[100]; int integerData; if (sscanf(buffer, "%[^:]:%[^:]:%d", string1, string2, &integerData) != 3) { fprintf(stderr, "解析父进程数据失败\n"); exit(EXIT_FAILURE); } printf("收到来自ex1的数据:\n"); printf("字符串1:%s\n", string1); printf("字符串2:%s\n", string2); printf("整数:%d\n", integerData); // 向父进程返回整数 int num = 556; ssize_t bytes_written = write(STDOUT_FILENO, &num, sizeof(int)); if (bytes_written == -1) { perror("write to parent"); exit(EXIT_FAILURE); } else if (bytes_written != sizeof(int)) { fprintf(stderr, "向父进程写入数据不完整\n"); exit(EXIT_FAILURE); } return 0; }
关键修正点
- 新增双向管道:用两个独立管道分别处理父到子、子到父的通信,解决单管道半双工的限制
- 修正子进程执行目标:将
execlp的执行路径改为./ex2,确保启动正确的程序 - 正确重定向IO:子进程将
stdin绑定到父→子管道读端,stdout绑定到子→父管道写端,同时关闭冗余的管道描述符 - 完善数据传输完整性:写入字符串时包含终止符,读取后手动补充终止符;检查读写字节数,避免不完整传输
- 调整读取时机:父进程先读取子进程返回数据,再等待子进程退出(顺序不影响,但需确保管道资源未被释放)
内容的提问来源于stack exchange,提问作者Tom James
相关产品推荐
相关产品推荐

