最近在调试嵌入式下设备启动基于busybox的init程序所执行的rc.sysinit脚本时,发现该脚本执行的后台脚本,会因为没有关联到串口设备(通过ps的TTY一列可以确认) ,导致我们做了tty设备检测的程序无法顺利执行,借此机会,查阅了busybox的init代码,并对linux的session group和controlling terminal有了一定的理解。

在libc的info文档中Job Control一节,有如下几个重要的定义:

  • process group,即进程组,用管道符创建的一堆命令就行成了一个进程组。
  • session,即会话,通常从login shell登陆后的所有进程都属于一个session,即session由login shell创建,注意libc的文档还是从传统的语义来讲的,对于现代的大多数桌面环境而言(DE,如KDE/GNOME等),一个图形会话,往往会通过setsid函数起大量的session),一个session下可以管理多个process group,一个进程可以在同一个session下的不同process group中迁移,但将一个进程移到另外的会话中则只能通过setsid这个函数来创建新的会话实现。
  • session leader,即会话leader,创建会话的进程称为会话leader,从上面的描述可知,login shell一般为会话leader,首次调用setsid的进程也将成为session leader。
  • controlling terminal,即控制终端,进程的重要属性之一,由fork创建的子进程继续父进程的controlling terminal,而从setsid的libc文档中可以看出(或者用man 3 setsid查看),setsid调用后,进程将做为新的会话的会话leader,并且丢失controlling terminal属性,而其后该会话leader打开的首个tty设备,将成为该会话的controlling terminal(见附2说明);shell通常只会将controlling terminal这个属性给予一个进程组,以便由这个进程组通过终端设备获取输入或者输出信息,这组进程将称为前台任务,而未获得输入或输出等权限的进程组,在尝试向controlling terminal读取或写入数据时,将收到SIGTTIN或SIGTTOU信号,默认情况下这个信号将中止相关程序的执行。这点是合理的,因为如果不做此限制,后台程序很可能扰乱终端输出或者输入的处理。

经过上述讲述,以代码形式给出上述概念的演示。

#include "stdlib.h"
#include "errno.h"
#include "unistd.h"
#include "fcntl.h"

int main()
{
    int err, fd;
    pid_t pid;
    char *pts_name;

    pid = fork();
    if (pid != 0) {
        exit(0); // 主进程退出。
    }

    pid = setsid(); // 在子进程中创建新的会话

    // 打印从父进程继续而来的tty,注意,该tty已经不是我们的controlling terminal
    printf("before we prepare the new stdio, the child inherit its parent's stdio %s\n", ttyname(0));

    // 如下一段代码用于创建一个pseudo-terminal,将作为tty设备被新的会话leader打开,成为该会话的controlling terminal
    fd = getpt();
    pts_name = ptsname(fd);
    printf("allocated a new pts is %s\n", pts_name); 
    grantpt(fd);
    unlockpt(fd);

    // 首次open的tty设备将成为controlling terminal
    fd = open(ptsname(fd), O_RDWR);

    // 将该fd做为标准输入,在本例中意义不大
    close(STDIN_FILENO);
    dup2(fd, STDIN_FILENO);

    // 停留2分,以便我们可以通过ps检验是否获得了新的session及controlling terminal
    sleep(120);
}

编译并执行上述程序后,执行后,立即用如下命令,可以查看到新进程确实拥有了新的controlling terminal了。

ps  -u fortitude -o pid,args,tt,sess,ppid

PID COMMAND                     TT        SESS  PPID
8086 ./sessionleader             pts/5     8086     1

参考文档:

  1. libc info,可在shell下info libc,或在emacs下info libc查看
  2. http://uw714doc.sco.com/en/SDK_sysprog/_The_Controlling-Terminal_and_Pr.html

    When a session-leader without a controlling-terminal opens a terminal-device-file and the flag O_NOCTTY is clear on open, that terminal becomes the controlling-terminal assigned to the session-leader if the terminal is not already assigned to some session (see open(2)). When any process other than a session-leader opens a terminal-device-file, or the flag O_NOCTTY is set on open, that terminal does not become the controlling-terminal assigned to the calling-process.


Comments

comments powered by Disqus