【问题标题】:Implementing unlimited pipes in C with just one loop and one pipe用一个循环和一个管道在 C 中实现无限管道
【发布时间】:2020-10-06 16:37:06
【问题描述】:

我正在尝试构建一个 minishell,但除了管道之外,我几乎什么都有。我在这个网站和许多其他网站上阅读了很多答案,但我无法找到我的特定问题。

序列应该在最右边的进程结束时结束,即:sleep 3 | sleep 3 | sleep 0 应该立即结束,而不是等待任何 sleep 3 进程。

*argvv 是指向第一个进程的指针(*argvv 将是sleep 3,*(argvv + 1) 是下一个sleep 3,等等。我不知道发生了什么,但有问题文件描述符和测试后一切都会中断。

例如,当输入为sh> ls | wc | wc时,输出为

sh> ls | wc | wc
sh>    1       3      25

,然后它等待一些输入。直到我再次按下回车它才完成。之后,sleep 3 就不再工作了。

欢迎任何帮助,非常感谢!

编辑:

好的,我编辑了代码,似乎一切正常,除了 sleep 3 | sleep 0 持续 3 秒,而不是立即结束。我不知道如何解决这个问题。

void execute(char ***argvv)
{
    pid_t pid;
    int pd[2];
    int in = 0;

    while (*argvv != NULL) {
        pipe(pd);
        
        if ((pid = fork()) < 0)
        {
            perror();
            exit(1);
        }
        else if (pid == 0) {
            dup2(in, 0);
            if (*(argvv + 1) != NULL)
                dup2(pf[1], 1);
            close(pd[0]);
            close(pd[1]);
            execvp((*argvv)[0], *argvv);
            perror();
            exit(1);
        }

        else
        {
            close(pd[1]);
            in = pd[0];

            if (*(argvv + 1) == NULL)
                close(pd[0]);
            argvv++;
        }
        wait(&pid);
    }

}

【问题讨论】:

  • 哎呀,three star programmerif (argvv == NULL) 我认为是错误的。 something is wrong with file descriptors and everything breaks after a test. 请说明什么是错误的,什么是中断的,以及你正在运行什么测试。请创建一个minimal reproducible example
  • @KamilCuk 明白了!我编辑了帖子,修复了您告诉我的内容并添加了一个示例。谢谢!
  • 提供的函数有点草率,因为它创建的管道比实际需要或使用的管道多,并且它使所有管道的读取端在父进程中无限期地打开。至少后者是一个严重的问题:它是一种资源泄漏形式,会很快导致损坏。
  • 还有一个 pid 泄漏,您没有提供使用 wait-family 功能之一收集每个孩子或将该职责转移到其他一些进程 - 例如,双重 @987654334 @ing 让流程 1 承担该责任。
  • 关于您上次的编辑,请阅读How should we treat posts that are vandalized by their original authors? 请不要通过破坏您的帖子来为他人增加工作量。通过在 Stack Overflow 上发帖,您已根据 CC BY-SA 4.0 许可授予 Stack Overflow 分发该内容的不可撤销权利。根据 Stack Overflow 政策,任何破坏行为都将被撤销。

标签: c shell while-loop pipe execvp


【解决方案1】:

我认为部分问题可能在于wait() 的使用。它的 pid 参数是一个 OUTPUT,报告 fork()ed 进程结束了。每个分叉处理结束,需要wait()ed 。当您运行第二个管道时,除了前面的一个管道之外,其他所有管道仍准备好被wait()ed for。

一般来说,你有几种选择:

  1. wait() 循环,直到您要结束的进程结束。
  2. wait() 循环,直到所有子进程结束。
  3. 使用waitpid() 查找特定进程的结束。
  4. 安排仅链中的最后一个进程是您的进程的子进程,使其余进程成为该进程的子进程。 (这是由某个 shell 完成的,虽然我不记得是哪个。)这样做的问题是它为链中的最后一个进程留下了额外的子进程,这可能会混淆它。
  5. 跟踪所有已结束的子进程。在后台运行管道时,这允许您显示管道的哪些部分仍在运行。 (tcsh 做到这一点。bash 做到了一半。)
  6. fork() 两次用于较早的管道成员,execvp() 在孙子中,然后在子中exit(),在继续之前让父 wait() 为子完成。这会断开孙子与父级的连接,因此永远不需要等待。
  7. 做一些无视SIGCHLD的事情,但这可能会让你很伤心,因为要么全有,要么全无。

【讨论】:

  • 嘿!感谢您的回答,感谢您的时间。这将如何解释管道不能使用两个命令但不能使用三个命令?它与两个管道中断并在最后一次执行后等待输入。 :(
  • @Lexuchrist 我之前错过了:你增加 argvv 太早了。或者,必须考虑其后 if 语句中的增量。
  • @Lexuchrist 也可能存在未充分关闭管道的问题。当您到达 wait() 时,不应保持打开状态。请注意,您可以在 dup2() 之后安全地关闭它。
  • 您好!我修复了一些东西(你可以在主帖上查看),它有点工作,但它并没有真正按预期工作。它应该在最右边的命令结束时结束,并且它当前等待sleep 3 结束。
  • 由于您将 wait() 调用移出条件,因此您还需要将其移出 while 循环。目前,您在完成后等待每个孩子,这意味着您等待睡眠 3,然后等待睡眠 0。如果第一个进程输出大量数据,它将被阻塞写入管道并且永远不会完成。你还在做 wait(&pid) ... 这仍然是一个输出变量。
猜你喜欢
  • 1970-01-01
  • 2020-04-04
  • 1970-01-01
  • 1970-01-01
  • 2014-03-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多