在fork子进程中exec外部程序后,如何向父进程传递数据?
当然有可行的方案!最直接且常用的是利用**管道(pipe)**来实现数据传递——因为fork()创建的子进程会继承父进程的文件描述符,而exec()替换进程代码时不会关闭未设置close-on-exec的FD,所以管道在exec后依然可用。下面给你两种具体的实现方式,按需选择:
方法1:重定向子进程的stdout到管道
这种方法不需要大幅修改program x的逻辑(只要x是往标准输出打印数据),适合x是现成程序的场景。
修改后的program y代码
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <string.h> int main(int argc, char *argv[]) { int fd[2]; // 创建管道,失败则报错退出 if (pipe(fd) == -1) { perror("pipe failed"); return 1; } pid_t p = fork(); if (p == -1) { perror("fork failed"); return 1; } if (p == 0) { // 子进程:关闭管道读端(我们只需要写数据) close(fd[0]); // 将管道写端重定向到stdout,这样x的输出会直接进入管道 if (dup2(fd[1], STDOUT_FILENO) == -1) { perror("dup2 failed"); return 1; } // 重定向完成后,关闭原有的管道写端 close(fd[1]); // 执行program x,参数按需调整 char *args[] = {"./program_x", "some_arg", NULL}; execv(args[0], args); // 如果execv返回,说明执行失败 perror("execv failed"); return 1; } else { // 父进程:关闭管道写端(我们只需要读数据) close(fd[1]); printf("We are in the parent process, waiting for child...\n"); wait(NULL); // 读取管道中的数据 char buffer[1024]; ssize_t bytes_read = read(fd[0], buffer, sizeof(buffer)-1); if (bytes_read == -1) { perror("read failed"); return 1; } // 确保字符串以'\0'结尾 buffer[bytes_read] = '\0'; printf("Parent received data: %s\n", buffer); close(fd[0]); } return 0; }
修改后的program x代码
#include <stdio.h> #include <string.h> #include <unistd.h> int main(int argc, char** argv) { if(argc != 2) { // 错误信息输出到stderr,避免混入管道数据 fprintf(stderr, "argument count does not match\n"); return -1; } printf("task1!\n"); char *value = "want this"; // 直接往stdout写,数据会通过管道传到父进程 printf("%s\n", value); // 也可以用write更直接: // write(STDOUT_FILENO, value, strlen(value)); return 0; }
方法2:将管道写端FD作为参数传给program x
这种方法适合需要让program x明确控制写入目标FD的场景,比如x需要同时输出到多个地方。
修改后的program y代码
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <string.h> #include <stdlib.h> int main(int argc, char *argv[]) { int fd[2]; if (pipe(fd) == -1) { perror("pipe failed"); return 1; } pid_t p = fork(); if (p == -1) { perror("fork failed"); return 1; } if (p == 0) { // 子进程:关闭读端 close(fd[0]); // 将管道写端的FD转为字符串,作为参数传给x char fd_str[10]; snprintf(fd_str, sizeof(fd_str), "%d", fd[1]); char *args[] = {"./program_x", "some_arg", fd_str, NULL}; execv(args[0], args); perror("execv failed"); return 1; } else { // 父进程:关闭写端 close(fd[1]); printf("We are in the parent process, waiting for child...\n"); wait(NULL); char buffer[1024]; ssize_t bytes_read = read(fd[0], buffer, sizeof(buffer)-1); if (bytes_read == -1) { perror("read failed"); return 1; } buffer[bytes_read] = '\0'; printf("Parent received data: %s\n", buffer); close(fd[0]); } return 0; }
修改后的program x代码
#include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char** argv) { // 现在需要3个参数:程序名、自定义参数、管道写端FD字符串 if(argc != 3) { fprintf(stderr, "argument count does not match\n"); return -1; } printf("task1!\n"); char *value = "want this"; // 将字符串形式的FD转为整数 int write_fd = atoi(argv[2]); // 往指定FD写入数据 write(write_fd, value, strlen(value)); // 关闭FD,养成好习惯 close(write_fd); return 0; }
关键注意事项
- 管道是单向的,父/子进程必须关闭自己不需要的管道端,否则可能导致
read阻塞(比如父进程没关写端,read会一直等待所有写端关闭)。 exec()系列函数成功执行后,后续代码不会运行,所以子进程中exec之后的close、write操作都是无效的,必须在exec前做好FD处理。- 如果传递大量数据,要注意管道缓冲区大小(通常几KB),可以通过循环读写避免数据丢失。
- 除了管道,还可以用共享内存、消息队列等IPC机制,但管道是这种场景下最简单直接的选择。
内容的提问来源于stack exchange,提问作者Irem
相关产品推荐
相关产品推荐

