shell C中多个进程之间的管道

unguejic  于 2023-02-13  发布在  Shell
关注(0)|答案(1)|浏览(142)

我正在用C写一个shell,并试图实现多个管道。我已经通过创建一个二维数组来实现这个目标,该数组带有管道,并且每次都使用一个单独的管道。管道之间的所有命令都被一个解析函数分隔开,并放入一个结构体中。管道之间的每个命令行都有自己的进程。对于中间的所有命令,我都使用“我正试着从上一个进程读取数据并写入下一个进程。2问题从这里开始。3它对一个管道工作正常,然而,当我尝试多个管道时,我没有得到任何输出,程序卡住了。在GDB中,我在分叉第二个进程后从execvp得到一个失败的消息。这可能是由于什么原因?

int create_pipe(int* fd)
{
  int pipe_id = pipe(fd); 
  if (pipe_id == -1)
  {
    return -1; 

  }
  return 0; 
}

void write_pipe(int* fd)
{
  close(fd[READ]);
  if ((dup2(fd[WRITE], STDOUT_FILENO)) < -1) 
  {
    fork_error(); 
  }
  close(fd[WRITE]);
 
}

void read_pipe(int *fd)
{
  close(fd[WRITE]);
  if (dup2(fd[READ], STDIN_FILENO) < 0)
  {
    fork_error();
  }
  close(fd[READ]);
  
}

void need_to_pipe (int i, int (*fd)[2])
{

  if (commands[i].pos == first)
  { 
    write_pipe(fd[i * 2]);
  }
  else if (commands[i].pos == last)
  {
    read_pipe(fd[(i-1) *2]); 
  }
  else //if (commands[i].pos == middle) 
  {
    
      dup2(fd[(i-1)*2][READ], STDIN_FILENO);
      close(fd[(i-1)*2][READ]);
      close(fd[(i-1)*2][WRITE]);
      //close(fd[(i)*2][READ]);
      //close(fd[(i)*2][WRITE]);
      close(fd[(i)*2][READ]);
      dup2(fd[i*2][WRITE], STDOUT_FILENO);
      close(fd[(i)*2][WRITE]); 
  }
  
  
}

/**
 *  Fork a proccess for command with index i in the command pipeline. If needed,
 *  create a new pipe and update the in and out members for the command..
 */
void fork_cmd(int i, int (*fd)[2]) {

  pid_t pid; 
  switch (pid = fork()) {
    case -1:
      fork_error();
    case 0:
      // Child process after a successful fork().
      if (!(commands[i].pos == single))
      {
        need_to_pipe(i, fd);
      }
      // Execute the command in the contex of the child process.
      if (execvp(commands[i].argv[0], commands[i].argv)<0)
      {
        
        fprintf(stderr, "command not found: %s\n", 
        commands[i].argv[0]);
        exit(EXIT_FAILURE);
      }

    default:

     
      // Parent process after a successful fork().
      break;
  }
}

/**
 *  Fork one child process for each command in the command pipeline.
 */
void fork_cmds(int n, int (*fd)[2]) 
{
  for (int i = 0; i < n; i++) 
  {
    fork_cmd(i, fd);
  }
}

void wait_once ()
{
  wait(NULL);
}

/**
 * Make the parents wait for all the child processes.
 */
void wait_for_all_cmds(int n) 
{
  for (int i = 0; i < n; i++)
  {
      wait_once();
     //wait for number of child processes. 
  }

}

int main() {
  int n;               // Number of commands in a command pipeline.
  size_t size = 128;   // Max size of a command line string.
  char line[size];        

  while(true) {
        // Buffer for a command line string.
    printf(" >>> ");

    get_line(line, size);

    n = parse_cmds(line, commands);

    int fd[(n-1)][2]; 
    for(int i =0;i<n-1;i++)
    {
      int pipe_id = pipe(fd[i*2]); 

      if (pipe_id == -1)
      {
        return -1; 

      }
    }

    fork_cmds(n, fd);

    for(int i =0;i<n-1;i++)
    {
      int *fdclose= fd[i*2]; 

      close (fdclose[READ]);
      close (fdclose[WRITE]);
    }
    
    wait_for_all_cmds(n);
    
  }

  exit(EXIT_SUCCESS);
}
zpjtge22

zpjtge221#

您[可能]有太多的进程保持管道末端打开(它们 * 不 * 属于给定的子进程),因为您的循环在 * 任何分叉之前 * 打开了 * 所有 * 管道。
这给每个子进程带来了不必要的负担,因为它必须关闭 * 许多 * 管道末端,以防止它保持管道末端打开,从而防止其他子进程在其输入管道上看到EOF。
要了解这一点,为了在当前代码中进行调试,子程序可以执行以下操作(就在exec*调用之前)(例如):

fprintf(stderr,"child: %d\n",getpid());
fflush(stderr);
system("ls -l /proc/self/fd 1>&2");

每个子节点在stdinstdoutstderr上只能有 * 三个 * 开放流(例如0、1、2)。
我想你会发现,有许多无关的/有害的流开放的各种儿童。
您只需要 * 两个 * 管道阵列(例如):int pipeinp[2]; int pipeout[2];最初,pipeinp都是-1。
大概...
父级应在fork_cmd的顶部[fork之前]对pipeout执行单个pipe调用。
子进程复制(并关闭)pipeinp的读取结束[如果不是-1]到stdin
子进程复制/关闭pipeoutstdout的写入端。
它关闭pipeout的读取端。
之后,父级应将pipeout拷贝到pipeinp,并关闭pipeinp的写入端
所有管道级均应重复上述操作。

  • 对于 last 命令,不应执行 * pipepipeout。并且,[last]子命令不应更改stdout

举个例子,请看我的回答:fd leak, custom Shell

相关问题