【问题标题】:Why is this not printing to standard output (stdout)?为什么这不打印到标准输出(stdout)?
【发布时间】:2016-02-07 08:02:59
【问题描述】:

我目前正在创建自己的命令行 shell,但在尝试接收管道时遇到了问题。我的程序从父进程开始。它检查用户是否输入了退出或历史记录。如果是这样,它会使用这些命令,但这并不重要。如果用户输入的不是退出或历史记录,那么它会创建一个执行当前任务的子进程。

但是,如果用户输入一个只有一个管道的命令,那么我们从父进程开始,它会创建一个子进程,称之为子进程,子进程 1。子进程 1 看到有一个管道并使用 fork 来创建另一个孩子,称此孩子 2。孩子 1 创建另一个孩子,称其为孩子 3(注意:孩子 2 和 3 使用共享内存)。现在,孩子 2 执行第一个命令,孩子 3 使用孩子 2 的输出执行命令。但由于某种原因,我没有得到任何输出。

我不确定这是否是执行任务的最有效方式,但这是我的教授所说的。

如果您想在这里查看我的所有代码,请访问:http://pastebin.com/YNTVf3XP

否则这是我从孩子 1 开始的代码:

            //Otherwise if we have a single pipe, then do
            else if (numPipes == 1) {

                char *command1[CMD_MAX];        //string holding the first command
                char *command2[CMD_MAX];        //string holding the second command
                int fds[2];                     //create file descriptors for the parent and child
                pid_t new_pid;                  //create new pid_t obj

                onePipeSplit(tokens, command1, command2, numTokens);    //Getting each command for pipe

                if (pipe(fds) < 0) {            //use the pipe command. If < 0
                    perror("Pipe failure.");    //we have an error, so exit
                    exit(EXIT_FAILURE);
                }

                //Creating child 2
                new_pid = fork();

                //if pid < 0 then we have an error. Exit.
                if (new_pid < 0) {
                    perror("Fork failure.");
                    exit(EXIT_FAILURE);
                }

                //Else we have the child (2) process
                else if (new_pid == 0) {

                    close(fds[0]);      //Close read

                    //sending stdin to the shared file descriptor
                    if (dup2(fds[1], STDOUT_FILENO)<0) {
                        perror("Can't dup");
                        exit(EXIT_FAILURE);
                    }

                    execvp(command1[0], command1);  //Execute the next command
                    perror("Exec failure");
                    exit(EXIT_FAILURE);
                }

                //Else if we have the parent process
                else {

                    pid_t child = fork();           //Creating child 3
                    if (new_pid < 0) {              //if pid < 0, error, exit
                        perror("Fork failure.");
                        exit(EXIT_FAILURE);
                    }

                    //else if pid > 0 we have the parent wait until children execute
                    else if (new_pid > 0) {
                        wait(NULL);
                    }

                    //else we have the child (3)
                    else {

                        close(fds[1]);      //Close write

                        //Sending stdout to the shared file descriptor
                        if (dup2(fds[0], STDIN_FILENO) < 0) {
                            perror("Can't dup");
                            exit(EXIT_FAILURE);
                        }

                        execvp(command2[0], command2);  //Execute the command
                        perror("Exec failure");
                        exit(EXIT_FAILURE);
                    }
                }
            }

这是我的教授给我的一张图片,用来展示它应该如何工作。

【问题讨论】:

  • 我觉得你的孩子太多了。如果您有foo | bar,您需要一个管道和另外两个进程(除了您的程序已经存在的那个)。所以父母创建了一个管道,p 父母派生了第一个孩子(这将是bar)。孩子:close(p[1]); dup2(p[0], 0); exec("bar"); 现在父母第二次分叉(这将是foo)。孩子:close(p[0]); dup2(p[1], 1); exec("foo"); 父母:close(p[0]); close[p[1]); wait for children.
  • @J.V.A.我在问题中添加了一张图片,显示了我的教授给了我什么。这不是意味着总共应该有3个孩子吗?不过我可能是错的。
  • 我不确定 shell(1) 的功能是什么,但如果教授希望这样做,那就是正确的答案:)
  • @j.v.a 好的,你明白为什么它没有打印到标准输出了吗?

标签: c debugging process pipe pid


【解决方案1】:

问题出在这里:

                   pid_t child = fork();           //Creating child 3
                    if (new_pid < 0) {   
       ... you keep checking `new_pid` here on down,
       ... but you should be checking `child` here on down...

同样在onePipeSplit 中,您需要在两个命令列表的末尾放置一个NULL,因为execvp 需要它。在第一个循环之后添加:

command1[i] = NULL;

第二个之后:

command2[i] = NULL;

好的,还有一些修复:

在每个 dup2() 之后,您需要关闭原始 fd。一个例子:

                    if (dup2(fds[1], STDOUT_FILENO)<0) {
                        perror("Can't dup");
                        exit(EXIT_FAILURE);
                    }
                    close(fds[1]);      /* ADD ME */

在父进程中:

                    //else if pid > 0 we have the parent wait until children execute
                    else if (child > 0) {
                        close(fds[0]);   /* we don't use */
                        close(fds[1]);   /* the pipe */
                        wait(NULL);
                        exit(0);         /* when 2 & 3 are done, we are too */
                    }

【讨论】:

  • 感谢您的回复。这修复了一个奇怪的错误,但不幸的是它仍然没有打印到标准输出。我会继续寻找,但到目前为止我不确定问题出在哪里。
  • 你也有第二个else if (new_pid &gt; 0) {,对吧?
  • 是的。我刚刚尝试摆脱第二个 fork,所以现在 shell(2) 执行第一个命令并管道到 shell(1) 并且它可以工作。这真的很奇怪,当我使用 2 个孩子时它不起作用......
  • 我认为如果您的教授希望这样做,您需要保留这 3 个过程。
  • 我绝对同意,我只是想我会尝试一下,看看错误是否在其他地方。
猜你喜欢
  • 2011-04-20
  • 1970-01-01
  • 2012-03-11
  • 2013-05-01
  • 1970-01-01
  • 2016-07-27
  • 2014-07-22
  • 1970-01-01
  • 2018-12-25
相关资源
最近更新 更多