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

forked processes with pipes having different output

我有一些代码练习分叉进程(在命令行中模拟 (|))。但是,每次的输出都不一样。例如,对于 ./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], 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], 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 可能会从自身读取数据。 (这似乎有时会发生)。进程从管道读取数据的顺序将取决于它们被安排到 运行.

的顺序

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