最近在调试嵌入式下设备启动基于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
参考文档:
- libc info,可在shell下info libc,或在emacs下info libc查看
- http://uw714doc.sco.com/en/SDK_sysprog/_The_Controlling-Terminal_and_Pr.htmlWhen 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.