【问题标题】:execl() does not seem to read from stdinexecl() 似乎没有从标准输入读取
【发布时间】:2016-03-22 00:18:49
【问题描述】:

我正在尝试用 c 语言重现这个命令:

ls | wc > output.txt

为此,我编写了以下程序:

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

int main()
{
    pid_t lsFork, wcFork;
    int tube[2];
    pipe(tube);

    lsFork = fork();
    if(lsFork == 0) // ls command
    {
        close(tube[0]);
        dup2(tube[1], STDOUT_FILENO);
        close(tube[1]);
        if(execl("/usr/bin/ls", "ls", NULL) == -1)
            perror("Cannot execute ls");
    }
    else
    {
        wcFork = fork();
        if(wcFork == 0)  // wc command
        {
            sleep(1);
            int file = open("output.txt", O_WRONLY | O_CREAT);
            if(file == -1)
                perror("Cannot open output.txt");

            close(tube[1]);
            dup2(tube[0], STDIN_FILENO);
            close(tube[0]);
            dup2(file, STDOUT_FILENO);
            close(file);

            /*char buffer[BUFSIZ];
            read(STDIN_FILENO, buffer, BUFSIZ);
            write(STDOUT_FILENO, buffer, BUFSIZ);*/

            if(execl("/usr/bin/wc", "wc", NULL) == -1)
                perror("Cannot execute wc");

            close(STDOUT_FILENO);
        }
        else // parent
        {
            int status;
            waitpid(lsFork, &status, 0);
            waitpid(wcFork, &status, 0);
        }
    }

    return EXIT_SUCCESS;
}

但是,程序并没有退出。根据 htop, wc 命令正在阻止程序。为了理解这种行为,我编写了一段代码(在 execl() 之前注释的行),但我不明白这是什么工作,而不是 execl()。我是不是在调用这个函数时忘记了什么?

【问题讨论】:

    标签: c pipe exec


    【解决方案1】:

    父进程仍然打开管道,因此wc 正在等待以防父进程决定写入内容(wc 需要计算在内)。

    在父节点中也关闭管道的两端:

        else // parent
        {
            int status;
            close(tube[0]); // <---
            close(tube[1]); // <---
            waitpid(lsFork, &status, 0);
            waitpid(wcFork, &status, 0);
        }
    

    【讨论】:

      【解决方案2】:

      当你可以轻松做到时,不要把事情复杂化。 试试下面更简单的代码,看看你能不能理解。

      int main(){
          int tube[2];
          int fID;
          pipe(tube);
          if (fork() == 0){
            // this is the child process
            close(tube[0]); // reading end of the pipe
            dup2(tube[1], 1); // stdout ---> pipe writing end
            execlp("ls", "ls", NULL);
          }else{
            if (fork() == 0){
              //umask(0022);
              fID = open("sample.txt", O_WRONLY | O_CREAT, 0644);
              close(tube[1]); // writing end of the pipe
              dup2(tube[0], 0);  // stdin ----> pipe reading end
              dup2(fID, 1);
              execlp("wc", "wc", NULL);
            }
          }
        return 0;
      }
      

      注意 如果代码的目的只是实现上述管道,那么您不需要实现任何等待机制。如果有的话,操作系统会自动杀死所有的僵尸孩子。此外execlp("wc", "wc", NULL); 将自动阻止程序结束。因此它不会提前退出

      【讨论】:

        【解决方案3】:

        您还需要在父级中关闭管道的写入端。

        【讨论】:

        • 父级处于阻塞模式,它没有处理甚至接触管道。关闭管道的写入端将没有效果也没有结果。
        • 管道(管);在 main 的第三行上,在父级中创建一个新管道。子进程获得父进程打开的 fds 的副本,包括管道的开放读写端。正如另一个答案中提到的那样,运行 wc 的分叉进程从输入 fd (在这种情况下是管道的输出端)读取直到 EOF,它永远不会收到它,因为尽管管道中的第一个进程终止了并非所有的写入端管道已关闭(因为它在父级中打开)。 -edit- 查看 man 2 管道了解更多信息:linux.die.net/man/2/pipe
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-30
        • 2012-02-17
        • 2022-01-21
        • 1970-01-01
        • 2015-07-05
        • 1970-01-01
        相关资源
        最近更新 更多