1、终端登录
BSD终端登录
a、系统管理员创建通常名为/etc/ttys的文件,其中,每个终端设备都有一行,每一行说明设备名和传递给getty程序的参数,例如,参数之一说明了终端的波特率等。
当程序自举时,内核创建进程ID为1的进程,也就是init进程。init进程使系统进入多用户状态。init进程读文件/etc/ttys,对每一个允许登录的终端设备,init调用一次fork,它所生成的子进程则执行(exec)getty程序。
fork and exec
init
------------------> getty
(现在为两个进程,init为getty的父进程,两个进程的实际用户ID和有效用户ID都为0)
b、getty为终端设备调用open函数,以读、写方式将终端打开。如果设备是调制解调器,则open可能会在设备终端驱动程序中滞留,直到用户拨号调制解调器,并且呼叫并应答。一旦设备被打开,则文件描述符0、1、2就被设置到该设备。然后getty输出"login:"之类的信息并等待用户键入用户名。
当用户键入了用户名后,getty的工作就完成了。然后它以类似于下面的方式调用login程序:
execle("/bin/login","login","-p",username,(char *)0,envp);
init以一个空环境调用getty。getty以终端名和在gettytab中说明的环境字符串为login创建一个环境(envp参数)。
fork and
exec
exec
init
------------------> getty ----------------> login
注意,现在还是只有两个进程,只不过之前的getty进程再次exec为login进程,getty和login进程ID相同,因为进程ID不会因执行exec而改变。并且,除了init进程,所有的父进程ID都为1.
login能执行多项工作,因为它得到了用户名,所以能调用getpwnam取得相应用户的口令文件登录项。然后调用getpass以显示"Password:",接着读用户键入的口令。如果用户几次键入的口令都无效,则login以参数1调用exit表示登录过程失败。父进程init了解到子进程的终止情况后,将再次调用fork,其后接着执行getty,对此终端重复以上过程。如果口令正确,则login进程完成一些登录用户的初始化工作,login进程的实际用户ID和有效用户ID改变为登录用户的用户ID(setuid)并调用该用户的登录shell。
c、到此为止,登录用户的登录shell开始执行。其父进程ID是init进程ID(进程ID为1),所以,当此登录shell终止时,init会得到通知(接到SIGCHLD信号),它会对该终端重复全部上述过程,将登录shell的文件描述符0、1、2设置为终端设备。
getty and
login
fd
0,1,2
硬链接
init
---------------------> shell <------------------> 终端设备驱动程序
<-------------> 终端用户
Linux终端登录
与BSD终端登录类似,在Linux中,/etc/inittab包含配置信息,它说明了init应当为之启动getty进程的各终端设备。
2、进程组
每个进程除了有一个进程ID之外,还属于一个进程组。进程组是一个或多个进程的集合。它们与同一作业相关联。每个进程组有一个唯一的进程组ID。
通常是由shell的管道线将几个进程编成一组的。如
proc1 | proc2 &。
每个进程组都可以有一个组长进程,组长进程的标识是,其进程组ID等于其进程ID。
组长进程可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程组中有一个进程存在,则该进程组就存在,这与其组长进程是否终止无关。进程组中的最后一个进程可以终止,或者转移到另一个进程组。
一个进程只能为它自己或它的子进程设置进程组ID。在它的子进程调用了exec函数之一后,它就不再能改变该子进程的进程组ID。
在大多数作业控制shell中(注意,这里提到作业控制shell,因为作业一般都与shell相关联),在fork之后调用setpgid函数,使父进程设置其子进程的进程组ID,并且使子进程设置其自己的进程组ID。这两个调用中有一个是冗余的,但让父子进程都这么做可以保证,在父、子进程认为子进程已进入了该进程组,这确实已经发生了。如果不这样做的话,那么fork之后,由于父、子进程运行先后次序的不确定,会造成在一段时间内(父、子进程只运行了其中一个)子进程组员身份的不确定(取决于哪个进程首先运行),这就产生了竞争条件。
3、孤儿进程组
定义:该组中每个成员的父进程要么是该组的一个成员,要么不是该组所属会话的成员。
另一种描述:一个进程组不是孤儿进程组的条件是,该组中有一个进程,其父进程在属于同一会话的另一个组中。