【问题标题】:Child not reading output from another child that put it in the pipe孩子没有读取另一个孩子的输出,将其放入管道
【发布时间】:2021-01-31 17:29:42
【问题描述】:

我现在一直在做这个学校作业,而且我非常接近完成。

任务是用 C 创建一个 bash shell,这听起来很基本,但它必须支持管道、IO 重定向和管道命令中的标志。除了一件事,我一切都在工作;的|管道子进程没有得到用户命令进程子进程写入管道的任何数据。如果我要删除 pipechild 的子分支,并让 if(pipe_cmd[0] != '\0') 中的所有内容作为父级运行,它会工作得很好(减去因为 execlp 而结束程序)。如果我要在管道部分中使用 printf(),输出将在正确的文件或终端中,这只会导致来自用户命令进程子进程的输入无法到达它需要的位置。

有人看到我如何使用管道的问题吗?考虑到管道的定义,这一切对我来说都是 100% 正常的。

        int a[2]; 
        pipe(a);
        //assume file_name is something like file.txt
        strcat(file_name, "file.txt");
        strcat(pipe_cmd, "wc");
        if(!fork()) 
        { 
            if(pipe_cmd[0] != '\0') // if there's a pipe
            {
                close(1); //close normal stdout
                dup(a[1]); // making stdout same as a[1] 
                close(a[0]); // closing other end of pipe 
                execlp("ls","ls",NULL);
            }
            else if(file_name[0] != '\0') // if just a bare command with a file redirect
            {
                int rootcmd_file = open(file_name, O_APPEND|O_WRONLY|O_CREAT, 0644);
                dup2(rootcmd_file, STDOUT_FILENO);
                execlp("ls","ls",NULL); // writes ls to the filename
            }
            // if no pipe or file name write... 
            else if(rootcmd_flags[0] != '\0') execlp("ls","ls",NULL)
            else execlp("ls","ls",NULL);
        } else wait(0);
        
        if(pipe_cmd[0] != '\0') // parent goes here, if pipe. 
        { 
            pipechild = fork();
            if(pipechild != 0) // *PROBLEM ARISES HERE- IF THIS IS FORKED, IT WILL HAVE NO INFO TAKEN IN.
            { 
                close(0); // closing normal stdin
                dup(a[0]); // making our input come from the child above
                close(a[1]); // close other end of pipe
                if(file_name[0] != '\0') // if a filename does exist, we must reroute the output to the pipe
                {
                    close(1); // close normal stdout
                    int fileredir_pipe = open(file_name, O_APPEND|O_WRONLY|O_CREAT, 0644);
                    dup2(fileredir_pipe, STDOUT_FILENO); //redirects STDOUT to file
                    execlp("wc","wc",NULL); // this outputs nothing
                }
                else
                {
                    // else there is no file. 
                    // executing the pipe in stdout using execlp.
                    execlp("wc","wc",NULL); // this outputs nothing

                }
            }
            else wait(0);
        }

提前致谢。对于某些代码被隐瞒,我深表歉意。这仍然是一项积极的任务,我不想要任何学术不诚实的案例。这个帖子风险太大了。

【问题讨论】:

  • 不幸的是,没有minimal reproducible example,没有人能够确定发生了什么。关键部分是未显示的部分。
  • 我遵循了第 2 部分,但如果需要,我可以尝试编写第二段代码。我删除了 pipechild 的子 fork() 代码并在没有它的情况下运行,并且由于某种原因,它工作得很好(减去 execlp() 应该杀死程序的事实)。我对此有点陌生,这还不够吗?
  • 抱歉,不是。除非世界上任何人都能够剪切/粘贴显示的代码,完全如图所示,然后编译、运行和重现您的问题,否则它不是minimal reproducible example .直到您对以下问题回答“是”:我可以仅将问题中的内容剪切/粘贴到新文件中,编译,运行并得到相同的问题 - 直到该问题的答案为“是”,这不是minimal reproducible example.
  • 好的,它已经编辑好了。它应该运行“ls | wc”,但“wc”部分没有从“ls”部分获得输入。希望能为您解决问题。
  • 这仍然不是minimal reproducible example,因为剪切/粘贴确切显示的内容没有编译的机会。没有包含文件。没有main,等等......不过,现在很明显这里出了什么问题......

标签: c pipe fork child-process


【解决方案1】:
} else wait(0);

显示的代码forks 是第一个子进程,然后waits 让它终止,此时。

第一个子进程在其标准输出上设置了一个管道。管道将连接到第二个子进程的标准输入。这个方案的致命缺陷是第二个子进程还没有启动,直到第一个进程终止才会启动。

管道的内部缓冲有限。如果第一个进程产生的输出很少,那么它的输出将适合微小的管道缓冲区,它会写入它的输出然后安静地终止,没有更明智的选择。

但是如果管道缓冲区已满,进程将阻塞并等待,直到从管道中读取某些内容并清除它。只要它发生,它就会等待。等等,等等,再等等。由于第二个子进程尚未启动,而父进程正在等待第一个进程终止,它会一直等待,徒劳无功。

由于这个原因,这个整体逻辑存在致命缺陷。正确的逻辑是完全fork并执行所有子进程,关闭父进程中的管道描述符(这也很重要),然后wait所有 个子进程终止。 wait 一定是这里发生的最后一件事,否则事情会以各种惊人而神秘的方式破裂。

【讨论】:

  • 我觉得我有点困惑。所以你说的是我在技术上不可能将管道用作缓冲区(尽管不是一个好主意)?要么,要么我应该 fork() 第一个孩子的 fork() 上方的第二个孩子,并使用那些守卫if 语句来确保执行流程运行第一个孩子,然后在不等待第一个孩子之后运行第二个孩子完成?我尝试了后者,结果和平常一样。
  • 我想我很清楚依赖管道内部缓冲区的大小是一个白日梦。我不知道你还尝试了什么,或者没有尝试。我只能知道问题中显示了什么,这是显示代码中的一个明显缺陷。此外,哪个进程首先获得forked 并不重要。我的回答中没有任何内容表明问题是forking 的相对顺序。最后,一旦两个进程都是forked,父进程就无法控制“执行流程”来运行一个或另一个子进程。它们现在是独立的进程。
  • 我认为是这样,但你说正确的逻辑是分叉并执行所有子进程,所以我认为分叉的顺序很重要。无论如何,这很遗憾现在不起作用,我认为我理解(​​并且仍然存在与以前相同的问题),但无论如何感谢您的关注。编辑:将 fork 更改为顶部(无论如何这样做更聪明),删除所有其他等待代码执行结束。仍然有同样的问题。也不能最低限度地重现该问题,当我使用所有相同的 IO 重定向自行尝试时,它工作正常。我在这里不知所措。
  • 看来你是对的。但我无法删除那个等待命令。如果我这样做了,那么 shell 命令提示符将无法正确打印。我想我将不得不完全重构我的代码。不想这样做,所以我可以接受它“有效”但并不完美。
  • 我明确解释说:“wait 让所有子进程终止”。没有人说要删除任何wait 电话。只是所有waiting 必须在forking 所有子进程之后发生。
猜你喜欢
  • 2018-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-18
  • 1970-01-01
  • 2018-05-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多