You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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();
}
问题分析与解决方案

核心问题

  1. 管道未传递给子进程:父进程创建的管道my_pipe在子进程调用execve后完全失效。execve会替换子进程的整个地址空间,原有的管道文件描述符信息丢失,而子进程自行创建了全新的管道,与父进程的管道无关联,自然接收不到数据。
  2. 野指针访问:子进程中的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 00:52:08