使用pipe与execve时子进程无法接收管道数据的问题
问题描述
尝试通过管道向子进程发送数据,fork后调用execve执行子进程程序正常,但子进程始终无法从管道接收任何字节。移除execve后程序运行正常,实际输出为0 0 0,预期输出为10 20 30。
父进程代码
#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/sysinfo.h> #include <sys/wait.h> #include <errno.h> #include <time.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <sys/stat.h> char *args_0[] = {"./child.out", "", (char *)0}; struct INFO { int a; int b; int c; }; pid_t child_pid; int i, j, status; int main() { struct INFO *info; ssize_t num_bytes; char buf[30]; int my_pipe[2]; info = malloc(sizeof(struct INFO)); if (pipe(my_pipe) == -1) { perror("error on pipe"); }; for (i = 0; i < 2; i++) { write(1, "fork\n", 5); switch (fork()) { case -1: write(1, "Error1\n", 7); exit(EXIT_FAILURE); case 0: write(1, "forked\n", 7); if (execve(args_0[0], args_0, NULL) == -1) { write(1, "Error\n", 6); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); break; default: break; } } info->a = 10; info->b = 20; info->c = 30; close(my_pipe[0]); for (size_t i = 0; i < 2; i++) { write(my_pipe[1], info, sizeof(struct INFO)); } close(my_pipe[1]); write(1, "writed\n", 7); /* Now let's wait for the termination of all kids */ while ((child_pid = wait(&status)) != -1) { /*printf("PARENT: PID=%d. Got info of child with PID=%d, status=0x%04X\n", getpid(), child_pid,status);*/ } exit(EXIT_SUCCESS); }
子进程代码
#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/sysinfo.h> #include <sys/wait.h> #include <errno.h> #include <time.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <sys/stat.h> struct INFO { int a; int b; int c; }; int main() { struct INFO* info; ssize_t num_bytes; char buf[30]; int my_pipe[2]; if (pipe(my_pipe) == -1) { perror("error on pipe"); }; close(my_pipe[1]); num_bytes = read(my_pipe[0], info, sizeof(struct INFO)); if (num_bytes >= 0) { bzero(buf, 30); snprintf(buf, 30, "num byte %ld\n",num_bytes); write(1, buf, 30); bzero(buf, 30); snprintf(buf, 30, "%d %d %d\n", info->a, info->b, info->c); write(1, buf, 30); } close(my_pipe[0]); write(1, "pausing\n", 8); pause(); }
问题分析与解决方案
核心问题
- 管道未传递给子进程:父进程创建的管道
my_pipe在子进程调用execve后完全失效。execve会替换子进程的整个地址空间,原有的管道文件描述符信息丢失,而子进程自行创建了全新的管道,与父进程的管道无关联,自然接收不到数据。 - 野指针访问:子进程中的
info指针未初始化,直接用read写入该指针指向的地址属于未定义行为,导致输出随机值或0 0 0。
修正步骤
父进程修改点
- 在
fork后的子进程分支中,将管道的读端文件描述符重定向到子进程的标准输入,确保execve后的程序能访问父进程创建的管道。 - 关闭子进程中不需要的管道写端。
子进程修改点
- 不再自行创建管道,直接从标准输入读取数据。
- 为
info分配内存(或使用栈上的结构体),避免野指针。
修正后的代码
父进程代码
#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <errno.h> #include <string.h> char *args_0[] = {"./child.out", (char *)0}; struct INFO { int a; int b; int c; }; int main() { struct INFO *info = malloc(sizeof(struct INFO)); int my_pipe[2]; if (pipe(my_pipe) == -1) { perror("error on pipe"); exit(EXIT_FAILURE); }; for (int i = 0; i < 2; i++) { write(1, "fork\n", 5); switch (fork()) { case -1: write(1, "Error1\n", 7); exit(EXIT_FAILURE); case 0: write(1, "forked\n", 7); // 关闭子进程不需要的写端 close(my_pipe[1]); // 将管道读端重定向到标准输入 dup2(my_pipe[0], STDIN_FILENO); close(my_pipe[0]); if (execve(args_0[0], args_0, NULL) == -1) { write(1, "Error\n", 6); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); break; default: break; } } // 父进程关闭读端 close(my_pipe[0]); info->a = 10; info->b = 20; info->c = 30; for (size_t i = 0; i < 2; i++) { write(my_pipe[1], info, sizeof(struct INFO)); } close(my_pipe[1]); write(1, "writed\n", 7); while (wait(NULL) != -1); free(info); exit(EXIT_SUCCESS); }
子进程代码
#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> struct INFO { int a; int b; int c; }; int main() { struct INFO info; // 使用栈上的结构体,避免野指针 ssize_t num_bytes; char buf[30]; // 直接从标准输入读取(父进程已将管道读端重定向到这里) num_bytes = read(STDIN_FILENO, &info, sizeof(struct INFO)); if (num_bytes > 0) { bzero(buf, 30); snprintf(buf, 30, "num byte %ld\n", num_bytes); write(1, buf, strlen(buf)); bzero(buf, 30); snprintf(buf, 30, "%d %d %d\n", info.a, info.b, info.c); write(1, buf, strlen(buf)); } else if (num_bytes == 0) { write(1, "pipe closed\n", 11); } else { perror("read error"); } write(1, "pausing\n", 8); pause(); return 0; }
内容的提问来源于stack exchange,提问作者Sako
相关产品推荐
相关产品推荐

