使用管道和execve未接收到字节

sczxawaw  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(88)

我尝试使用管道将一些数据发送到子进程。分叉后,我调用execve,它可以正常工作,但我仍然无法从管道接收任何字节。我不知道我错过了什么。我尝试在不使用execve的情况下构建相同的程序,它可以正常工作。
家长代码:

#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();
}


我应该得到
10 20 30
但我得到了
0 0 0

3pmvbmvn

3pmvbmvn1#

你的代码中的主要问题是,父级和子级都打开了两个独立的管道,它们不会以任何方式相互干扰。因此,父级写入一个从未被读取的管道,而子级从另一个从未被写入任何内容的管道读取。
首先,这是设计缺陷的结果。如果两个进程紧密相关,需要从同一个匿名管道中读取数据,那么它们(通常)应该托管在同一个应用程序中(也就是说,你可以fork,但不是通过exec[...]家族的任何函数启动另一个应用程序,而是直接执行子进程)。
一个例外是,当你想通过标准IO与第三方应用程序通信时;然后你会创建 * 两个 * 管道,fork,dup2相应的管道结束于标准IO文件描述符:

dup2(in_pipe_in[1], STDOUT_FILENO);
dup2(out_pipe[0], STDIN_FILENO);

字符串
并关闭子进程中的所有管道末端(相关的管道末端已经被复制了!),而在父进程中只有两个未使用的管道末端,in_pipe[1]out_pipe[0]。注意:上面的命名反映了父进程的观点(它仍然会重用管道),因此看起来从in到out的Map是冲突的,反之亦然。
实际上,你也可以在你的双应用程序场景中使用这种方法,子应用程序将使用标准输入/输出与父应用程序进行通信(printfscanfputs都可以使用,但read(STDIN_FILENO, ...)write(STDOUT_FILENO, ...)也可以用于二进制数据)。
最后,如果您不能或不想将文件描述符复制到标准IO,(也许你需要另一个目的),那么你需要让子进程知道父进程的文件描述符。(您将获得父进程映像的副本,其中只有调用fork的结果不同),然而,所有这些信息在调用exec[...]家族的任何函数时都会丢失,其中包含当前进程的映像的映像完全被要加载的应用程序之一替换。(尽管文件描述符保持打开,这就是为什么复制标准IO工作,见上文,工作)。
有几种方法,包括命令行参数,环境变量或临时文件-其中提到的第一个对我来说似乎是最狭窄的一个,例如:

char* path = ...; // to the child executable
char in_fd[32];   // naming according to child
char out_fd[32];
snprintf(in_fd, sizeof(in_fd), "%d", out_pipe[0]);
snprintf(out_fd, sizeof(out_fd), "%d", in_pipe[1]);
execl(path, path, in_fd, out_fd, (char const*) NULL);


然后在子进程中,将命令行参数解析回整数,并使用这些参数进行阅读和写入(跳过与创建自己的管道相关的所有部分)。(不完整)示例演示了双向通信;两个方向中的每一个都需要两个单独的管道(父到子和子到父);当然,对于单向通信,您可以删除不必要的管道。
请注意,您的代码在子级中显示了另一个致命错误,在父级中显示了内存泄漏:在子元素中,您为数据定义一个指针(struct INFO* info;),但你从来没有给它分配任何有意义的值,而是将read分配给未初始化的指针,产生未定义的行为。(可能是程序崩溃,但也可能发生其他任何事情,例如程序显示出一些不合理的行为,可能在一些看起来完全不相关的地方,甚至-看起来-运行良好),而在父对象中,你不需要再次free你之前malloc ed的内存。你可以通过简单地使用本地分配的对象来避免这两种情况:

struct INFO info; // no pointer!
write(out_pipe[1], &info, sizeof(info)); // parent;
read(in_fd, &info, sizeof(info)); // child
// note how the address is taken from the object via &!


最后一点:这里提供的所有代码都是完全未经测试的;如果你发现一个bug,请自行修复。

相关问题