微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

具有不同输出的管道的分叉进程

如何解决具有不同输出的管道的分叉进程

我有一些代码练习分叉进程(在命令行中模拟 (|))。但是,每次的输出都不相同。例如,对于 ./pipe ls cat wc 的输入,它应该与 ls | cat | wc 相同。但是,有时我的代码输出

Child with PID 12126 exited with status 0x0.

Child with PID 12127 exited with status 0x0.

      7       7      52

Child with PID 12128 exited with status 0x0.

但有时它也会输出

Makefile

pipe

#pipe.c#

pipe.c

pipe.o

README.md

test

Child with PID 12138 exited with status 0x0.

Child with PID 12139 exited with status 0x0.

      0       0       0

Child with PID 12140 exited with status 0x0.

一个输出是正确的(与ls | cat | wc相比)。我认为通过第二个输出,程序 lscat 的管道输出没有被 wc 处理。我想知道我的程序出了什么问题,因为似乎我正确设置了管道 - 第一个程序将从 stdin 获取输入并输出到管道的写入端,最后一个程序将从读取端获取输入管道和输出到标准输出。任何输入表示赞赏。

代码./pipe):

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

int main(int argc,char *argv[])
{
  int fd[2];
  if (pipe(fd) == -1) {
    int err = errno;
    perror("pipe");
    return err;
  }
  pid_t pid[argc-1];
  int n = argc;
  if(argc <= 1){
    return EINVAL;
  }
  for (int i = 1; i < argc; i++){
    if ((pid[i] = fork()) == -1){
      int err = errno;
      perror("fork");
      return err;
    }else if(pid[i] == 0){

      //open(fd[1]);
      if(i == 1){
        dup2(fd[1],STDOUT_FILENO);
        if (execlp(argv[i],argv[i],NULL) == -1) {
          printf("Failed to search for the provided executed program. \n");
          return errno;
        }
        
        
      }else if(i == argc-1){
        dup2(fd[0],STDIN_FILENO);
        if (execlp(argv[i],NULL) == -1) {
          printf("Failed to search for the provided executed program. \n");
          return errno;
        }
      }else{
          dup2(fd[0],STDIN_FILENO);
          dup2(fd[1],STDOUT_FILENO);
          if (execlp(argv[i],NULL) == -1) {
            printf("Failed to search for the provided executed program. \n");
            return errno;
          }
      }
      
    }
     close(fd[1]);
  }
  
  int wstatus;
  pid_t pids;
  while (n > 1) {
    pids = wait(&wstatus);
    printf("Child with PID %ld exited with status 0x%x.\n",(long)pids,wstatus);
    --n;
  }

}

解决方法

您正在越界访问可变长度数组 pid,因此您的程序具有未定义行为

您可以通过将数组放大一个元素来解决这个问题:

pid_t pid[argc]; /* not argc - 1 */

另一个问题是每对命令之间需要一个新管道。总共,一个管道比命令的数量少。也就是说,一个命令的管道数为零。

stdin - ls - pipe - cat - pipe - wc - stdout   // 3 commands,2 pipes

这里有一个想法,我在(几乎)循环的每次迭代中创建一个管道,并为下一次迭代保存管道的输入端。我根本不费心保存进程 id:s 并且我没有为 dup2 添加错误检查。但是,您应该检查它是否真的成功。

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

int exec(const char* prog) {
    if(execlp(prog,prog,NULL) == -1) {
        perror("execlp");
    }
    return 1;
}

int main(int argc,char* argv[]) {
    pid_t pid;

    int in; // for saving the input side of the pipe between iterations

    for(int i = 1; i < argc; ++i) {
        int fd[2];

        // must have at least 2 commands to create a pipe
        // and we need one pipe less than the number of commands
        if(argc > 1 && i < argc - 1) pipe(fd);

        if((pid = fork()) == -1) {
            perror("fork");
            return 1;

        } else if(pid == 0) {
            if(i == 1) {       // first command
                if(argc > 1) { // must have more than one command to dup
                    dup2(fd[1],STDOUT_FILENO);
                    close(fd[1]);
                    close(fd[0]);
                }

            } else if(i == argc - 1) { // last command
                dup2(in,STDIN_FILENO);
                close(in);

            } else { // middle commands
                dup2(in,STDIN_FILENO);
                close(in);
                dup2(fd[1],STDOUT_FILENO);
                close(fd[1]);
                close(fd[0]);
            }

            return exec(argv[i]);
        }

        // parent
        if(i > 1) close(in);  // close old in
        in = fd[0]; // save the stdin side of the pipe for next loop iteration
        close(fd[1]); // no use for the stdout end
    }

    int wstatus;
    while((pid = wait(&wstatus)) != -1) {
        printf("Child with PID %ld exited with status 0x%x.\n",(long)pid,wstatus); 
    }
}
,

您需要更多管道,因为您当前的代码中只有一个。如果 lscat 都在写入该管道,并且 catwc 都从该管道读取,则您无法同步 cat 读取哪些数据wc 读取的数据。也就是说,wc 可能直接从 lscat 读取数据。而 cat 可能会从自身读取数据。 (这似乎是有时会发生的事情)。进程从管道读取数据的顺序取决于它们被安排运行的顺序。

你需要更多的管道。 (比进程总数少一个。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。