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(バスはプロセス番号1)、BashがPID 1で実行されているときに、フォアグラウンドジョブがジョブ制御信号を無視するのはなぜですか?何か特別なことがあると思います。プロセス番号1

修正する:

とても簡単な殻を見つけました。裏打ちジョブ制御もgithubで実装されており、わずか949行です! init1とinit2を使用してこのシェルを実行すると、同じ問題が発生します! (おかげで問題を解決するために複雑なbashソースコードを読む必要はありませんでした。Orz)問題はSIGTSTP(^ Z)が到着したときにすぐに返されないwaitpid()にあります。したがって、この問題はbashだけでなく、ジョブ制御を実装するシェルにも関連しています。しかし、シェルがPID 1で実行されているときにSIGTSTPが到着した場合、waitpid()が返されない理由はわかりません。

ベストアンサー1

おすすめ記事