繁体   English   中英

dup2() 和 fork 后无法从 pipe 读取。 C

[英]Can't read from a pipe after dup2() and fork. C

我正在编写一个echo显字符串和sed两次的代码。 我的 output 是正确的,但是当我尝试将该字符串放在数组上时,它会阻止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 个字符串传递给 function output 就像:

1
1
1

如果我不重复到最后一个 pipe 它可以很好地打印我需要的东西,我还在最后一个 fork 中使用了 return 因为它是唯一没有被execvp杀死的子进程。 但它甚至没有达到第一次印刷。 我什至尝试将 pipe 作为文件或经典打开方式打开,所以我尝试了 open 和fopen ,如您所见。 我失败了,因为它无法读取任何内容。 那将是一个时间问题。

分叉和文件描述符

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

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

最小更改量

因此,您的代码获得结果的最小更改次数如下。

如果第 41 行中的第一个分叉成功,那么在父级中您需要关闭 pipe 文件描述符 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();

当你现在运行你的代码时,你已经得到了 output:

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之前执行,代码会更容易理解)

这给出了 output 然后:

1
2
hello world

所以 sed 命令都被执行了。

附加说明

以下是一些没有特定顺序的注释,主要涉及错误处理。

  • fork的调用返回pid_t而不是 int。 因此,您应该将变量 pid 的定义更改为: pid_t pid; .

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

    perror("execvp of command xyz failed");

    exit(EXIT_FAILURE);

  • 如果打开 pipe 失败,还要在 stderr 上打印一条描述性消息。

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

  • 在 main 中,您应该返回成功或失败 state(例如return EXIT_SUCCESS; )。

  • 您不使用变量read_ 然后可以删除变量。

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

  • 分配了 realloc 的 memory 永远不会被释放。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM