Apue.2e Chapter9 Process relations
终端登录过程:
init读取/ect/ttys,对每一个允许登陆的中断设备fork一次,子进程执行getty程序,getty为终端设备调用open函数(read | write),打开后设置filedes 0,1,2;getty输出login等待用户输入用户名,然后调用login程序(execle传入环境变量)。后者使用getpwnam获得用户口令登陆项,再调用getpass显示提示符提示用户输入密码,然后用crypt将输入的口令加密,再与阴影口令文件中的pw_passwd字段作比较,确定密码的正确性。如果错误,调用exit离开;如果正确,chdir,chown,chmod,setgid,initgroups,setuid,execl /bin/sh,初始化环境变量等等,shell会读取启动文件,然后才能输出shell提示符。
现代UNIX系统支持PAM(可插入式身份验证模块),允许admin配置身份验证方法。
网络登陆:
init调用一个shell,然后执行/etc/rc,启动守护进程inetd,该进程等待TCP/IP请求到达主机,然后fork并执行适当的进程。这里使用了伪终端来模拟login的登陆过程,父进程处理网络通信,子进程则执行login程序(同传统登陆)。
进程组:
pid_t getpgrp(void);
pid_t getpgid(pid_t pid);
int setpgid(pid_t pid,pid_t pgid);
进程组id是创建该进程组的进程的id,也就是组长id。
注意:一个进程只能为自己或子进程设置pid,一旦在子进程中调用了exec系函数,就不能再改变进程组id。为防止竞争条件,往往在父子进程fork后都执行一次setpgid(冗余一次)。
会话(session):
会话是进程组的集合,会话id是非Posix的,只有会话首进程(session leader)id,也就是创建会话的进程的id。
pid_t getsid(pid_t pid);
如果pid是0,返回调用进程的会话首进程的进程组的pid。如果pid不属于调用者所在的会话,返回错误。
pid_t setsid(void);
建立新会话,如果调用该函数的进程已经是一个进程组的组长,则调用失败。新建的绘画没有控制终端。
控制终端:
会话的非必需关联物,前台进程组是正在控制终端中运行的进程组,后台则相反。
可以通过以下函数获得前台进程组:
pid_t tcgetpgrp(int filedes);
int tcsetpgrp(int filedes, pid_t pgrpid);
注意:pgrpid必须是同一会话中的进程组的id;
作业控制:
这部分主要讲了常见的作业控制方法(前台、后台等),以及终端驱动程序如何与之交互。
使用stty可以改变作业控制相关的输出方式;
shell执行程序:
具有作业控制和不具有作业控制的shell执行管道命令的方式不同。
具有作业控制的shell会将前台作业放入它自己的进程组(和shell不同),而不具有作业控制的则相同。
孤儿进程组:
进程组的每个成员的父进程要么是该组的一个成员,要么不是该组所属会话中的成员。
在父进程终止后,Posix.1要求向新的孤儿进程组中处于停止状态的每一个进程发送挂断信号(SIGHUP),接着又向其发送继续信号。
最后讲了FreeBSD的实现。