5、Session
一个session是一个或者多个进程组的集合。参考资料中给出了一个简单的图来表示这个意思。
这里不给出图了,叙述一下图中描述的情况:
有一个session,session中有三个进程组。第一个进程组中有一个login shell进程;第二个进程组中有两个进程:proc1,proc2;第三个进程组中有三个进程:proc3,proc4,proc5。
在个进程组中的进程,一般都是通过shell管道的方式来产生的,上面叙述的图中的后两个进程组是通过如下的命令产生的:
proc1 | proc2 &
proc3 | proc4 | proc5
一个进程是通过调用函数setsid来建立一个会话(session)的。这个函数如下:
#include <unistd.h>
pid_t setsid(void);
这个函数正确的时候返回进程组id,错误的时候返回1(其值实际为-1)。
如果调用这个函数的进程不是一个进程组leader,那么这个函数创建一个新的session,这时候有如下三个事情发生:
- 进程变成这个新session的session leader(一个session leader就是创建这个session的进程),并且这个进程成为这个新的session中的唯一的进程。
- 进程变成一个新的进程组的leader。这个新的进程组id就是这个调用setsid函数的进程的进程id。
- 进程没有controlling terminal(控制终端)。我们将在下一节讨论控制终端。如果再进程调用setsid函数之前这个进程具有了一个控制终端,那么和那个控制终端的联系会被断开。
如果调用进程已经是一个进程组的leader了,那么这个函数这个函数会返回一个错误。为了确保不会发生这种情况,一般的方法是调用fork创建一个子进程,然后让父进程终止,子进程继续;这样我们可以保证子进程不是一个进程组leader,因为子进程会自动继承父进程的进程组id,但是子进程的进程号却是新产生的(和父进程不同)(所以子进程的pid就不等于父进程的pid,所以也不等于其进程组的组id了,因为组id和组leader的pid相等).
Single UNIX Specification只说了"session leader",并没有和进程ID和进程组ID类似的"session ID"的说法。很显然,一个session leader就是一个单个的进程,所以我们说到 session ID的时候,就认为那是这个session的session leader的进程pid。session ID的概念是在SVR4中引入的。BSD体系的系统没有支持这个概念,但是已经被更新包含了这个相关的东西。 getsid函数返回一个进程的session leader的的进程组id。getsid函数在Single UNIX Specification中被作为XSI扩展包含进去了。
Solaris和Single UNIX Specification差不多,它尽量避免使用"session id"的概念,它采用的说法是"process group ID of the session leader".这两种说法是相等的,一个session 的leader一直都是一个process group的leader。
获取session id的函数:
#include <unistd.h>
pid_t getsid(pid_t pid);
如果正确会返回session leader的process group ID,错误的时候返回1(其值实际为-1)。
如果pid是0,那么getsid返回调用进程的session的进程组id。由于某些安全性的因素,有些实现可能会在pid参数和进程id不属于同一个session的情况下限制调用进程获取相应的"session id"。
译者注
原文参考
6、控制终端
会话和进程组有一些其它的特性:
- 会话可以有一个单个的控制终端。这个控制终端一般都是我们登陆时候的终端设备(在终端登陆的情况下),或者是一个伪终端设备(在网络登陆的情况下)。
- 建立到控制终端连接的会话leader被称作控制进程。
- 在一个会话中的进程组可以被分成一个单一的前台进程组,以及一个或者多个后台进程组。
- 如果一个session有一个控制终端,那么它有一个单一的前台进程组,这个session(session就是会话)其他的进程组是后台进程组。
- 当我们键入终端的中断键(一般都是DELETE或者Control-C)的时候,会发送给所有在前台进程组中的进程一个中断信号。
- 当我们敲入终端的quit键(一般是Control-backslash)的时候,这会导致一个quit信号发送给所有前台进程组的进程。
- 如果一个modem(或者网络)的连接断开被终端的接口检测到了,那么会给控制进程(session leader)发送一个hang-up信号。
参考资料给出了一个图描述了这个叙述。这里不给出原图形了,简单总结原图的意思就是:
- controlling terminal在检测到modem disconnect之类的情况的时候会给login shell(也是后台进程组中的进程,也是session leader,也是控制进程)发送hang-up信号。
- 终端输入或者终端发起的信号会发送给session中的前台进程组。
- 另外,一个session中还有可能存在其他的后台进程组。
一般来说,我们不用担心控制终端,因为在我们登陆进去的时候它会自动被建立。
POSIX.1把分配控制终端机制留给了各自实现。
从UNIX System V继承过来的系统,会在session leader打开第一个没有和session建立联系的终端设备的时候分配一个控制终端.当然这假设session leader调用的open没有指定O_NOCTTY标志。
基于BSD的系统会在session leader调用ioctl(有一个TIOCSCTTY请求参数,第三个参数是空指针)的时候给一个session分配一个控制终端。在调用之前,session不能已经有了controlling terminal(否则调用不成功).一般来说,这个ioctl调用后面需要接着一个setsid调用,这个调用保证进程是一个没有控制终端的session leader.在BSD系列的系统中,POSIX.1中open的O_NOCTTY标记并没有使用,除非是要和其他系统相互兼容的时候。
有时候一个程序想要和controlling terminal进行交互,并且不考虑标准输入输出是否被重新定向了。程序用来确保自己是要和controlling terminal进行交互的方法是通过打开/dev/tty文件.这个特殊文件在内核中和controlling terminal是同义的。一般来说,如果程序没有controlling terminal,那么打开这个设备文件会失败。
一个经典的例子是getpass函数,这个函数读取密码(这时候terminal 的echo是关闭的).这个函数被crypt程序调用,可以在管道中使用。例如:
crypt < salaries | lpr
解密salaries文件然后通过管道输出到打印池。因为crypt从标准输入读取输入文件,不用使用标准输入输入密码。并且,crypt程序每次运行的时候,需要输入加密的密码,这样我们就不用将密码保存在文件中了(保存在文件中有安全隐患)。
有一些已知的方法将crypt使用的encoding给break,这里不说了。
网友评论