【问题标题】:Executing the ls | wc linux command in c program using 2 processes communicating trough a pipe执行 ls | c程序中的wc linux命令使用2个通过管道进行通信的进程
【发布时间】:2022-01-13 02:55:08
【问题描述】:

我目前在以下练习中遇到问题:

我想用下面的程序在 linux bash 中“模仿”管道命令行ls | wc。 我要做的是:

  • 创建管道
  • 创建一个 reader child 和 writer child
  • 作家子关闭管道的读取端并将其标准输出重定向到管道的写入端
  • reader child 关闭管道的写入端并将管道的读取端作为标准输入
  • 两个孩子都执行 exec,编写器执行 ls 程序,将输出通过管道传递给读取器,读取器在该输出上执行 wc 程序。

当我在 linux 终端中执行 ls | wc 时,我得到以下结果:

8      8     101

但是如果我执行我的程序,我会得到以下结果:

0      0      0

这是我的程序:

#include <stdlib.h> 
#include <errno.h> 
#include <stdio.h>
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <string.h> 
#include <sys/wait.h>
#include <libgen.h>
#include <signal.h>
#include <errno.h>

int main(void){
        int mypipe[2];
        pid_t pid1, pid2;

        if (pipe(mypipe)<0)
                perror ("pipe error"), exit(1);
        if ((pid1=fork())<0)
                perror ("fork error"), exit(1);

        else if (pid1==0) {
                //reader child
                close (mypipe[1]);
                if (dup2(mypipe[0], STDIN_FILENO)!=STDIN_FILENO)
                        perror ("dup2 error"), exit(1);
                close (mypipe[0]); 
                if (execlp("wc", "wc", NULL)<0)
                        perror("execlp1 error"), exit(1);
                else {  //pid >0, parent
                        if ((pid2=fork())<0)
                                perror ("fork error"), exit(2);
                        else if (pid2==0) {     
                                //writer child
                                close(mypipe[0]);
                                if (dup2(mypipe[1], STDOUT_FILENO) != STDOUT_FILENO)
                                        perror("dup2 error"), exit(1);
                                close (mypipe[1]);
                                if (execlp("ls", "ls", NULL)<0)
                                        perror ("execlp error"), exit(1);
                        }
                        else {  //parent
                                close(mypipe[0]);
                                close(mypipe[1]);

                                waitpid(pid1, NULL, 0);
                                waitpid(pid2, NULL, 0);
                                exit(0);
                        }
                }
        }
return 0;
}

我做错了什么?提前感谢您的回答!

【问题讨论】:

  • 请注意,如果execlp()(或任何其他exec*()函数)成功,它不会返回;如果返回,则失败。无需测试返回值。虽然在这种情况下它可能无关紧要,但通常最好在打印错误后发送exit()_exit()
  • if (execlp("wc", "wc", NULL)&lt;0)execlp 成功时,该行之后的所有内容都不会运行。但是你在ifelse 中有第二个fork。代码缩进应该提醒你这样做。那不是你想要的。您需要将else { //pid &gt;0, parentelse if (pid1==0) 配对。

标签: c linux process pipe dup


【解决方案1】:

您的代码很混乱。你有很多不相关的标题,重复#include &lt;errno.h&gt;。在main() 函数中,您有:

int main(void)
{
    int mypipe[2];
    pid_t pid1, pid2;

    if (pipe(mypipe) < 0)
        perror("pipe error"), exit(1);
    if ((pid1 = fork()) < 0)
        perror("fork error"), exit(1);
    else if (pid1 == 0)
    {   
        // reader child
        close(mypipe[1]);
        if (dup2(mypipe[0], STDIN_FILENO) != STDIN_FILENO)
            perror("dup2 error"), exit(1);
        close(mypipe[0]);
        if (execlp("wc", "wc", NULL) < 0)
            perror("execlp1 error"), exit(1);
        else            // pid >0, parent
        {   
            …
        }
    }
    return 0;
}

else if (pid1 == 0) 子句由子执行。它关闭管道的写入端,将读取端复制到标准输入并关闭管道的读取端。然后它在wc 上执行execlp()。只有当代码无法执行wc 时才会执行else 子句,然后只有管道的读取端保持打开状态。同时,原始进程简单地退出。这将关闭管道,因此wc 命令没有输入,并因此报告0 0 0

您需要重写代码。父进程应该等到它的两个子进程都执行。尤其是在调试的时候,千万不要忽略children的退出状态,要上报。

这里有一些有效的代码。请注意,它避免了浓密的决策树——它是if / else if / ... / else 代码的线性序列。一般来说,这比一堆复杂的、多层次的条件更容易理解。

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
    int fd[2];
    pid_t pid1, pid2;

    if (pipe(fd) < 0)
        perror("pipe error"), exit(1);
    else if ((pid1 = fork()) < 0)
        perror("fork error"), exit(1);
    else if (pid1 == 0)
    {
        /* ls */
        dup2(fd[1], STDOUT_FILENO);
        close(fd[0]);
        close(fd[1]);
        execlp("ls", "ls", (char *)0);
        perror("exec ls");
        exit(1);
    }
    else if ((pid2 = fork()) < 0)
        perror("fork error"), exit(1);
    else if (pid2 == 0)
    {
        /* wc */
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        close(fd[1]);
        execlp("wc", "wc", (char *)0);
        perror("exec wc");
        exit(1);
    }
    else
    {
        close(fd[0]);
        close(fd[1]);
        int status1;
        int status2;
        int corpse1 = waitpid(pid1, &status1, 0);
        int corpse2 = waitpid(pid2, &status2, 0);
        printf("ls: pid = %d, corpse = %d, exit status = 0x%.4X\n", pid1, corpse1, status1);
        printf("ls: pid = %d, corpse = %d, exit status = 0x%.4X\n", pid2, corpse2, status2);
    }
    return 0;
}

在我的机器上运行程序pipe41 的示例:

$ pipe41
     175     175    1954
ls: pid = 44770, corpse = 44770, exit status = 0x0000
ls: pid = 44771, corpse = 44771, exit status = 0x0000
$ ls | wc
     175     175    1954
$

【讨论】:

  • 非常感谢!我会珍惜你所有的指正,从现在开始我会写出最清晰的代码!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-10
  • 1970-01-01
  • 2011-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多