Конвейерная команда ps -j |все еще висит после закрытия всех каналов и изменения PGID - PullRequest
0 голосов
/ 10 февраля 2019

Я пытаюсь создать свою собственную оболочку.Идея состоит в том, что, когда я делаю многоступенчатую конвейерную команду, такую ​​как ps -j | more, первый процесс ps -j будет лидером группы процессов, а все последующие процессы будут использовать один и тот же идентификатор группы процессов.

Для некоторыхпричина, это работает для команд как ls | grep pipes, но зависает на ps -j | more.

Вот изображение, чтобы продемонстрировать:

enter image description here

Вещи, которые я сделал:

  • Закрытие всех каналов в секунду, когда я совершаю любой dup2() вызов как для детей, так и для родителей
  • Вызов setpgid() перед любым execvp() вызовом ив и дочерний и родительский

Код ниже.Краткое объяснение того, как это работает: каждый процесс вызывает fork_process(), который принимает строку позиции: FIRST | MIDDLE | FINAL | ONLY, которая указывает, какой процесс находится в конвейере.

Например, ps-j = FIRST и more = FINAL.

fork_process() затем создает каналы, разветвления и выполняет команду в дочернем процессе.Это также, где setpgid() называется.Если разветвленный процесс является FIRST или ONLY, то он сохраняет свой pid в cmd_group->pgid, поэтому другой процесс может setpgid() для этого pgid.

execute_commands :

int execute_commands(Command_Group* cmd_group) {
  int cmd_count = cmd_group->cmd_count;
  int fd_count = cmd_group->fd_count;
  int pipe_count = cmd_group->pipe_count;  

  for (int i = 0; i < cmd_count; i++) {

    if (i == 0 && pipe_count == 0 && cmd_count == 1) {
      if (fork_process(cmd_group, i, NULL, NULL, ONLY) < 0) {
        perror("invalid error executing command");
        return -1;
      }
    }
    else if (i == 0 && pipe_count > 0) { 
      int* fd = cmd_group->pipe_fds[fd_count++];
      if (fork_process(cmd_group, i, fd, NULL, FIRST) < 0) {
        perror("invalid error executing command");
        return -1;
      }
    }
    else if (i == (cmd_count - 1) && pipe_count > 0) {
      int* fd = cmd_group->pipe_fds[fd_count - 1];
      if (fork_process(cmd_group, i, fd, NULL, FINAL) < 0) {
        perror("invalid error executing command");
        return -1;
      }
    } 
    else {
      int* previous_fd = cmd_group->pipe_fds[fd_count - 1];
      int* fd = cmd_group->pipe_fds[fd_count++];
      if (fork_process(cmd_group, i, previous_fd, fd, MIDDLE) < 0) {
        perror("invalid error executing command");
        return -1;
      }
    }
  }
  close_pipes(cmd_group->pipe_fds, pipe_count);

  printf("PARENT pid: %d, pgid: %d, ppid: %d\n", getpid(), getpgid(0), getppid());

  for (int i = 0; i < cmd_group->cmd_count; i++) {
    int pid = cmd_group->cmd_list[i]->pid;
    char* cmd = cmd_group->cmd_list[i]->cmd;
    printf("CHILD cmd: %s, pid: %d, pgid: %d\n", cmd, pid, getpgid(pid));
  }


  int status;
  int corpse;

  while ((corpse = waitpid(-1, &status, 0)) > 0) {
    fprintf(stderr, "%d: child %d exited with status 0x%.4X\n",
              (int)getpid(), corpse, (unsigned)status);
  }

  return 0;
}

fork_process :

int fork_process(Command_Group* cmd_group, int cmd_idx, int fd[2], int second_fd[2], char* position) {

  if (cmd_group == NULL) {
    perror("invalid null command passed to fork_process");
    return -1;
  }

  Command* cmd = cmd_group->cmd_list[cmd_idx];

  pid_t pid;

  if (strcmp(position, FIRST) == 0) {
    pipe(fd);
  } else if (strcmp(position, MIDDLE) == 0) {
    pipe(second_fd);
  }

  if ((pid = fork()) < 0) {
    perror("invalid fork command\n");
    exit(EXIT_FAILURE);
  } else if (pid == 0) { // Child
    if (strcmp(position, FIRST) == 0) {
      if (dup2(fd[OUT], STDOUT_FILENO) < 0) {
        perror("invalid piping: dup2 failure");
        return -1;
      }
      if (setpgid(pid, pid) < 0) {
        perror("invalid setpgid failure");
      }
    } 
    else if (strcmp(position, MIDDLE) == 0) {
      if (dup2(fd[IN], STDIN_FILENO) < 0 || dup2(second_fd[OUT], STDOUT_FILENO) < 0) {
        perror("invalid piping: dup2 failure");
        return -1;
      }
      if (setpgid(pid, cmd_group->pgid) < 0) {
        perror("invalid setpgid failure");
      }
    } 
    else if (strcmp(position, FINAL) == 0) {
      if (dup2(fd[IN], STDIN_FILENO) < 0) {
        perror("invalid piping: dup2 failure");
        return -1;
      }
      if (setpgid(pid, cmd_group->pgid) < 0) {
        perror("invalid setpgid failure");
      }
    }
    close_pipes(cmd_group->pipe_fds, cmd_group->pipe_count);

    if (create_redirects(cmd) < 0) {
      perror("invalid redirection error");
      return -1;
    }

    if (execvp(cmd->cmd, cmd->args) < 0) {
      perror("invalid: execvp failure");
      return -1;
    }
  }
  // Parent
  if (strcmp(position, FIRST) == 0 || strcmp(position, ONLY) == 0) {
    setpgid(pid, pid);
    cmd_group->pgid = pid;
  } else {  
    setpgid(pid, cmd_group->pgid);
  }
  cmd->pid = pid;

  return 0;
}
...