如果作为 init 调用,为什么 bash 不能将 ^C 映射到 intr 信号?

如果作为 init 调用,为什么 bash 不能将 ^C 映射到 intr 信号?

如果我将 bash 作为第一个调用的进程(即作为 init),结果它将显示以下内容:

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

并且没有信号(例如^C、^Z)起作用。

通过阅读bash-5.1.12的源码,问题定位在job.c4501行的表达式上:

(t = tcgetpgrp (shell_tty)) == -1

错误值为ENOTTY,表示调用进程没有控制终端。

为什么 Bash 在作为 init 调用时没有控制终端?

答案1

控制终端bash是由 准备(打开)的login。因此,如果bash直接调用 initskiing login,则会抛出上面的问题,因为控制终端在处理信号方面起着特殊的作用。

根据源代码登录.c(中util-linux),调用过程bash大致login如下:

  1. main() 调用 fork_session()

    /*
     * Detach the controlling terminal, fork, and create a new session
     * and reinitialize syslog stuff.
     */
    fork_session(&cxt);
    
  2. fork 一个新进程。(在fork_session()功能上)

    child_pid = fork();
    
  3. 使新进程成为会话领导者。 为了获得控制终端,该进程必须先成为会话领导者。 (在fork_session()功能上)

    /* start new session */
    setsid();
    
  4. 获取会话领导者的控制 tty。open会话领导者通过tty获取控制终端。 (在fork_session()功能上)

    /* make sure we have a controlling tty */
    open_tty(cxt->tty_path);
    openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */
    
    /*
     * TIOCSCTTY: steal tty from other process group.
     */
    if (ioctl(0, TIOCSCTTY, 1))
        syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
    
  5. 在新进程中执行登录 shell。(返回main()

    /* if the shell field has a space: treat it like a shell script */
    if (strchr(pwd->pw_shell, ' ')) {
       char *buff;
    
       xasprintf(&buff, "exec %s", pwd->pw_shell);
       child_argv[child_argc++] = "/bin/sh";
       child_argv[child_argc++] = "-sh";
       child_argv[child_argc++] = "-c";
       child_argv[child_argc++] = buff;
    } else {
       char tbuf[PATH_MAX + 2], *p;
    
       tbuf[0] = '-';
       xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
                   p + 1 : pwd->pw_shell), sizeof(tbuf) - 1);
    
       child_argv[child_argc++] = pwd->pw_shell;
       child_argv[child_argc++] = xstrdup(tbuf);
    }
    
    child_argv[child_argc++] = NULL;
    
    /* http://www.linux-pam.org/Linux-PAM-html/adg-interface-by-app-expected.html#adg-pam_end */
    (void) pam_end(cxt.pamh, PAM_SUCCESS|PAM_DATA_SILENT);
    
    execvp(child_argv[0], child_argv + 1);
    

更多关于概念的链接控制终端

相关内容