dup2()和fork之后无法从管道中读取。C

问题描述 投票:-2回答:1

我正在写一个代码,它可以 echosed 两次。我的输出是正确的,但当我试图将该字符串放置在一个数组上时,它在 read 并继续进行其他调用。

这是代码。

#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
char **sendout=NULL;
int send_i=0;

void sender2(char* str_) {
    int fd[2];
    int fd1[2];
    int fd2[2];
    int pid;
    char* echo[] = {"echo", str_, NULL};
    char* sed[] = {"sed", "regex1", NULL};
    char* sed2[] = {"sed", "regex2", NULL};
    int status;

    if (pipe(fd) < 0) {
        exit(100);
    }

    pid = fork();
    if (pid == 0) {
        close(fd[0]);

        dup2(fd[1], 1);

        close(fd[1]);
        execvp(echo[0], echo);
        printf("Error in execvp1\n");
    }

    if (pipe(fd1) < 0) {
        exit(100);
    }

    pid = fork();
    if (pid == 0) {
        close(fd[1]);
        close(fd1[0]);

        dup2(fd[0], 0);
        dup2(fd1[1], 1);
        dup2(fd1[1], 2);

        close(fd[0]);
        close(fd1[1]);
        execvp(sed2[0], sed2);
        printf("Error in execvp2\n");
    }

    if (pipe(fd2) < 0) {
        exit(100);
    }

    pid = fork();
    if (pid == 0) {
        close(fd1[1]);
        close(fd2[0]);

        dup2(fd1[0], 0);
        dup2(fd2[1], 1);
        dup2(fd2[1], 2);

        close(fd2[1]);
        close(fd1[0]);
        execvp(sed[0], sed);
    }

    pid = fork();
    if (pid == 0) {
        close(fd2[1]);

        char* line = NULL;
        size_t len = 0;
        ssize_t read_;
        FILE* f_pipe;
        f_pipe = fdopen(fd2[0], "r");
        printf("1\n");
        while ((read_ = getline(&line, &len, f_pipe)) != -1) {
            printf("2\n");
            sendout = realloc(sendout, sizeof(char*) * (send_i + 1));
            sendout[send_i] = strdup(line);
            send_i++;
            printf("%s\n", line);
        }

        fclose(f_pipe);

        close(fd2[0]);
        return;
    }

    close(fd[1]);
    close(fd[0]);
    close(fd1[1]);
    close(fd1[0]);
    close(fd2[1]);
    close(fd2[0]);
    if (pid != 0) {
        wait(&status);
    }
}

int main() { 
sender2("hello"); 
}

就像我说的那样,一切都很正常,直到... read. 如果我把3个字符串传给函数,输出是这样的。

1
1
1

如果我不在最后一根管子上重复,它就能很好地打印出我所需要的东西,我还在最后一个分叉中使用了return,因为它是唯一一个没有被杀死的子进程。execvp. 但它甚至没有达到第一个打印。我甚至尝试打开管道作为一个文件或与一个经典的打开,所以它去,我试着打开,还 fopen如你所见 我失败了,因为它无法读取任何东西。这将是一个时间问题。

c pipe fork exec dup
1个回答
0
投票

叉子和文件描述符

当你fork一个进程时,所有文件描述符的副本都会被继承。由于这些都是副本,所以在子进程和父进程中都必须关闭描述符。你应该总是尽快地关闭它们。如果你多次分叉,这一点尤其正确。

这里很容易漏掉一些东西。因此,最好仔细检查所有文件描述符是否已经关闭。

最小修改量

所以,你的代码要想得到一个结果,最少的修改次数如下。

如果第41行的第一次分叉成功,那么在父文件中你需要关闭管道文件描述符fd[0]和fd[1],例如第56行。

pid = fork();
if (pid == 0) {
   ...
}

close(fd[0]); //<-- add these two lines
close(fd[1]);

if (pipe(fd2) < 0) {
    ...

同样的,你也需要在fd1的第二次分叉后做同样的事情,所以。

pid = fork();
if (pid == 0) {
    ...
}

close(fd1[0]); //<-- add these two lines
close(fd1[1]);


pid = fork();

当你现在运行你的代码时,你已经得到了这样的输出:

1
2
hello

更好的测试案例

这还不能验证这两个 sed 命令将正确运行。在测试用例中,将main中的调用改为。

sender2("hello mars");

并将 sed 命令改为:

char* sed[] = {"sed", "s/moon/world/", NULL};
char* sed2[] = {"sed", "s/mars/moon/", NULL};

(sed2 命令在 sed 在你的代码中,如果能在你的代码中加入 sed 前执行 sed2)

这样就会得到输出。

1
2
hello world

因此,两个 sed 命令都被执行。

补充说明

以下是一些言论,不分先后,主要是关于错误处理。

  • 呼吁 fork 返回 pid_t 而不是int。所以你应该把变量pid的定义改为: pid_t pid;.

  • 如果 execvp 失败,应该打印错误原因,并以错误状态退出,例如这样。

    perror("execvp of command xyz failed");

    exit(EXIT_FAILURE);

  • 如果打开管道失败,也要在stderr上打印一条描述性信息。

  • 同时 fork 调用可能失败,这也应该被处理。在这种情况下,fork返回-1。和上面一样,在stderr上打印错误信息并返回错误状态。

  • 在main中,你应该返回一个成功或失败的状态(如 return EXIT_SUCCESS;).

  • 你不需要使用变量 read_. 然后就可以删除该变量。

  • 如果 fdopen 失败,它返回NULL。这种错误情况应该被处理。

  • 用realloc分配的内存永远不会被释放。

© www.soinside.com 2019 - 2024. All rights reserved.