fork创建的C进程间stdin/stdout通信异常:右子进程返回垃圾值
问题分析与解决
问题描述
需要创建两个子进程left和right,分别执行./left和./right程序:
left从标准输入读取两个整数求和,将结果通过标准输出传回父进程right接收left的结果再加10后,同样通过标准输出传回父进程
当前代码中left子进程结果正常,但right子进程返回垃圾值,预期输出Right child result: 25,实际得到随机值。
问题代码
main.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { //###### 0 is read end, 1 is write end int p2c_l[2]; // Parent to child left pipe, p2c[0] is read end, p2c[1] is write end from parent perspective int c2p_l[2]; // Child to parent left pipe // Create pipes pipe(p2c_l); pipe(c2p_l); int pid = fork(); if (pid == 0) { //Left child fprintf(stderr, "Left child process\n"); // close right child pipes close(c2p_l[0]); close(p2c_l[1]); dup2(p2c_l[0], 0); dup2(c2p_l[1], 1); char* args[] = {"./left", NULL}; execvp(args[0], args); fprintf(stderr, "Left child process failed\n"); exit(EXIT_FAILURE); } else { // Parent process close(c2p_l[1]); close(p2c_l[0]); // dup2(p2c_l[1], 1); // dup2(c2p_l[0], 0); fprintf(stderr, "Parent process after first fork\n"); int n1, n2; n1 = 5; n2 = 10; write(p2c_l[1], &n1, sizeof(n1)); write(p2c_l[1], &n2, sizeof(n2)); int resultFromLeftChild; read(c2p_l[0], &resultFromLeftChild, sizeof(resultFromLeftChild)); fprintf(stderr, "Left child result: %d\n", resultFromLeftChild); int p2c_r[2]; // Parent to child right pipe int c2p_r[2]; // Child to parent right pipe int pid2 = fork(); if (pid2 == 0) { //Right child fprintf(stderr, "Right child process\n"); // close left child pipes close(p2c_r[0]); close(c2p_r[1]); //make pc2_r[0] and c2p_r[1] as stdin and stdout dup2(p2c_r[0], 0); dup2(c2p_r[1], 1); char* args[] = {"./right", NULL}; execvp(args[0], args); fprintf(stderr, "Right child process failed\n"); exit(EXIT_FAILURE); } else { // Parent process close(p2c_r[1]); close(c2p_r[0]); // dup2(p2c_r[1], 1); // dup2(c2p_r[0], 0); fprintf(stderr, "Parent process after second fork\n"); int n1, n2; n1 = resultFromLeftChild; n2 = 10; // printf("%d\n", n1); // printf("%d\n", n2); write(p2c_r[1], &n1, sizeof(n1)); write(p2c_r[1], &n2, sizeof(n2)); int resultFromRightChild; read(c2p_r[0], &resultFromRightChild, sizeof(resultFromRightChild)); fprintf(stderr, "Right child result: %d\n", resultFromRightChild); int result = resultFromLeftChild + resultFromRightChild; fprintf(stderr, "Result: %d\n", result); } } return 0; }
left.c 和 right.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { int num1, num2; if (argc != 1) { printf("Usage: %s \n", argv[0]); return 1; // Error code for incorrect usage } scanf("%d", &num1); scanf("%d", &num2); // Calculate the addition int result = num1 + num2; //printf("Inputs: %d %d \n", num1, num2); // Print the result printf("%d\n", result); return 0; // Successful execution }
问题根源
1. Right子进程管道操作完全反向
管道两端的功能被搞反:
- 对于
p2c_r(父→子管道):子进程需要从p2c_r[0](读端)读取数据,应关闭p2c_r[1](写端),但原代码关闭了p2c_r[0],导致子进程无法读取父进程发送的数据。 - 对于
c2p_r(子→父管道):子进程需要往c2p_r[1](写端)写入结果,应关闭c2p_r[0](读端),但原代码关闭了c2p_r[1],导致子进程无法向父进程发送数据。
2. 父进程处理Right管道时关闭了错误的端
父进程需要向p2c_r[1](写端)写入数据,却错误关闭了该端;虽然读取c2p_r[0](读端)的逻辑正确,但由于子进程管道已失效,最终读取到的是垃圾值。
3. 管道数据格式不匹配(隐藏问题)
父进程用write直接发送二进制格式的int,但子进程用scanf读取文本格式的整数,两者不兼容。left子进程看似正常只是巧合,实际scanf会因无法识别二进制数据读取失败,num1和num2会是未初始化的随机值。
修复后的代码
修复方案说明
- 修正Right子进程和父进程的管道关闭逻辑
- 统一管道数据格式:改用文本格式传输,父进程用
fprintf写入数字,子进程保持scanf读取逻辑不变
修复后的main.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { int p2c_l[2]; int c2p_l[2]; pipe(p2c_l); pipe(c2p_l); int pid = fork(); if (pid == 0) { // Left child fprintf(stderr, "Left child process\n"); close(c2p_l[0]); close(p2c_l[1]); dup2(p2c_l[0], 0); dup2(c2p_l[1], 1); char* args[] = {"./left", NULL}; execvp(args[0], args); fprintf(stderr, "Left child process failed\n"); exit(EXIT_FAILURE); } else { // Parent process close(c2p_l[1]); close(p2c_l[0]); fprintf(stderr, "Parent process after first fork\n"); // 改用文本格式写入管道 FILE* p2c_l_file = fdopen(p2c_l[1], "w"); fprintf(p2c_l_file, "%d\n%d\n", 5, 10); fflush(p2c_l_file); fclose(p2c_l_file); // 关闭后子进程的scanf会读到EOF,结束读取 // 读取left子进程的文本结果并转为int char left_result_buf[32]; read(c2p_l[0], left_result_buf, sizeof(left_result_buf)); int resultFromLeftChild = atoi(left_result_buf); fprintf(stderr, "Left child result: %d\n", resultFromLeftChild); close(c2p_l[0]); int p2c_r[2]; int c2p_r[2]; pipe(p2c_r); pipe(c2p_r); int pid2 = fork(); if (pid2 == 0) { // Right child fprintf(stderr, "Right child process\n"); // 修正管道关闭逻辑:关闭不需要的端 close(p2c_r[1]); close(c2p_r[0]); dup2(p2c_r[0], 0); dup2(c2p_r[1], 1); char* args[] = {"./right", NULL}; execvp(args[0], args); fprintf(stderr, "Right child process failed\n"); exit(EXIT_FAILURE); } else { // Parent process // 修正管道关闭逻辑:关闭不需要的端 close(p2c_r[0]); close(c2p_r[1]); fprintf(stderr, "Parent process after second fork\n"); // 文本格式写入数据 FILE* p2c_r_file = fdopen(p2c_r[1], "w"); fprintf(p2c_r_file, "%d\n%d\n", resultFromLeftChild, 10); fflush(p2c_r_file); fclose(p2c_r_file); // 读取right子进程的文本结果 char right_result_buf[32]; read(c2p_r[0], right_result_buf, sizeof(right_result_buf)); int resultFromRightChild = atoi(right_result_buf); fprintf(stderr, "Right child result: %d\n", resultFromRightChild); int result = resultFromLeftChild + resultFromRightChild; fprintf(stderr, "Result: %d\n", result); close(p2c_r[1]); close(c2p_r[0]); wait(NULL); // 等待right子进程结束 } wait(NULL); // 等待left子进程结束 } return 0; }
修复后的输出
Parent process after first fork Left child process Left child result: 15 Parent process after second fork Right child process Right child result: 25 Result: 40
内容的提问来源于stack exchange,提问作者Emre Kırmızı
相关产品推荐
相关产品推荐

