Bash가 PID 1로 실행될 때 포그라운드 작업이 작업 제어 신호를 무시하는 이유는 무엇입니까?

Bash가 PID 1로 실행될 때 포그라운드 작업이 작업 제어 신호를 무시하는 이유는 무엇입니까?

bash다음과 같이 호출될 때프로세스 번호 1커널 옵션을 통해 직접 init=/bin/bash --login메시지를 표시하기 전에 다음과 같은 내용을 표시합니다.

bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell

그리고키보드 생성 신호(예: ^Z, ^C, ^)가 작동하지 않습니다..

이 문제를 해결하기 위해 다음과 같은 간단한 프로그램 init1.c(예: 단순화됨 sulogin)을 작성했습니다.

/* init1.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

int main(int argc, char **argv)
{
  char *options[] = {"--login", NULL};
  int tty_fd = -1;

  printf("\n----- Bash Init 1 -----\n\n");

  /* Make bash as session leader. */
  if (setsid() == -1)
    {
      fprintf(stderr, "%s : %d : %s\n", "setsid()", __LINE__, strerror(errno));
      exit(EXIT_FAILURE);
    }

  /* Make /dev/tty1 as controlling terminal of Bash. */
  tty_fd = open("/dev/tty1", O_RDWR);
  if (tty_fd == -1)
    {
      fprintf(stderr, "%s : %d : %s\n", "open()", __LINE__, strerror(errno));
      exit(EXIT_FAILURE);
    }

  /* Re-connect stdin, stdout, stderr to the new tty. */
  dup2(tty_fd, STDIN_FILENO);
  dup2(tty_fd, STDOUT_FILENO);
  dup2(tty_fd, STDERR_FILENO);
  close(tty_fd);
  
  execv("/bin/bash", options);
}

다음과 같이 컴파일한 init1다음 다음과 같이 호출합니다.프로세스 번호 1(즉, Bash는 다음과 같이 실행됩니다.프로세스 번호 1), 이전 오류 메시지가 사라지고 일부 신호(예: Ctrl-C, Ctrl-\)는 작동하지만 작업 제어 신호(예: Ctrl-Z)는 여전히 작동하지 않습니다(예기치 않게).

따라서 작업 제어 신호가 작동하도록 하기 위해 위 코드를 init2.c다음과 같이 수정했습니다 fork().

/* init2.c */
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

int main(int argc, char **argv)
{
  char *options[] = {"--login", NULL};
  pid_t pid = -1;
  int tty_fd = -1;
  
  printf("\n----- Bash Init 2 -----\n\n");
  
  pid = fork();
  if (pid < 0)
    {
      fprintf(stderr, "%s : %d : %s\n", "fork()", __LINE__, strerror(errno));
      exit(EXIT_FAILURE);
    }
    
  /* Parent. */
  if (pid > 0)
   {
     /* Wait for its child, otherwise all processes would be killed ! */
     while (wait(NULL) > 0)
       ;
     exit(EXIT_SUCCESS);
   }
      
  /* Child. */
  if (setsid() == -1)
    {
      fprintf(stderr, "%s : %d : %s\n", "setsid()", __LINE__, strerror(errno));
      exit(EXIT_FAILURE);
    }        
  
  /* Make /dev/tty1 as controlling terminal of Bash. */
  tty_fd = open("/dev/tty1", O_RDWR);
  if (tty_fd == -1)
    {
      fprintf(stderr, "%s : %d : %s\n", "open()", __LINE__, strerror(errno));
      exit(EXIT_FAILURE);
    }

  /* Re-connect stdin, stdout, stderr to the new tty. */
  dup2(tty_fd, STDIN_FILENO);
  dup2(tty_fd, STDOUT_FILENO);
  dup2(tty_fd, STDERR_FILENO);
  close(tty_fd);
  
  execv("/bin/bash", options);
}

다음과 같이 컴파일하고 init2다음과 같이 호출합니다.프로세스 번호 1(즉, Bash는 1을 제외한 모든 PID로 실행됩니다.) 이번에는 작업 제어 신호가 작동합니다!

하지만 왜 작업 제어 신호가 있는지 이해가 되지 않습니다 init2(Bash는 그렇지 않습니다).프로세스 번호 1) 그러나 그렇지 않습니다 init1(Bash는프로세스 번호 1), Bash가 PID 1로 실행될 때 포그라운드 작업이 작업 제어 신호를 무시하는 이유는 무엇입니까? 뭔가 특별한 게 있는 것 같아요프로세스 번호 1.

고쳐 쓰다:

아주 간단한 껍질을 찾았어요뒤죽박죽작업 제어도 github에서 구현되며 단 949줄입니다! init1 및 init2를 사용하여 이 쉘을 실행할 때 동일한 문제가 발생합니다! (덕분에 문제를 해결하기 위해 복잡한 bash 소스 코드를 읽을 필요가 없었습니다. Orz) 문제는 SIGTSTP(^Z)가 도착할 때 즉시 반환되지 않는 waitpid()에 있습니다. 따라서 이 문제는 bash뿐만 아니라 작업 제어를 구현하는 셸에도 관련이 있습니다. 그러나 쉘이 PID 1로 실행 중일 때 SIGTSTP가 도착하면 waitpid()가 반환되지 않는 이유를 이해할 수 없습니다.

관련 정보