第九章 进程关系
9.2 终端登录
终端类型:基于字符的终端、仿真基于字符终端的图形终端、运行窗口系统的图形终端
BSD终端登录如下:
系统启动时,内核创建init进程(进程号为1),init进程使系统进入多用户模式。init读取文件/etc/ttys(每个终端设备都占有一行,每一行说明设备名和传到getty程序的参数),对每一个允许登录的终端设备,init调用一次fork,它所生成的子进程以空环境exec getty程序。
getty对终端设备调用open函数,以读写方式将终端打开(文件描述符0,1,2关联到终端设备),然后getty输出“login:"之类的信息,并等待用户输入用户名。当用户输入用户名之后,getty程序的工作就做完了。
getty程序完成之后,调用login程序:
execle("/bin/login", "login", "-p", username, (char*)0, envp);login可以用于验证密码,并且完成以下工作:
- 将当前工作目录更改为该用户的起始目录
- 调用chown更改该终端的所有权,使登录用户成为它的所有者
- 将对终端设备的访问权限改变为”用户读和写“
- 调用 setgid 以及 initgroups 设置进程的组ID
- 用login得到的所有信息初始化环境
- login进程更改为登录用户的用户ID(setuid)并且调用该用户的登录shell:
execl("/bin/sh", "-sh", (char*)0);
总流程:
init(进程ID为1)
--fork-->init(进程ID不为1)
--exec-->getty(打开终端设备,读用户名,初始环境集)
--exec-->login(...)
--exec-->登录shell
<--fd 0,1,2-->终端设备驱动
<--硬连接-->终端用户
9.3 网络登录
BSD网络登录如下:
init进程调用shell,使其执行shell脚本/etc/rc。此脚本启动守护进程inetd。一旦此shell脚本终止,inetd的父进程就变成init。inetd等待网络连接到达主机。
当网络请求到来时,inetd执行fork,然后子进程exec。
例:在TELNET服务进程的TCP连接请求到达时:即telnet hostname
- telnetd进程打开一个伪终端设备,并用fork分成两个进程。父进程处理网路连接的通信,子进程执行login程序。父进程和子进程通过伪终端相连接。子进程在调用exec login之前,子进程使其文件描述符0,1,2与伪终端相连。登录正确的话,login执行和终端登录中login相同的步骤。然后login调用exec将其自身替换为登录shell。
init-->inetd-->inetd-->telnetd-->login-->登录shell<--->伪终端设备驱动<-->终端用户
9.4 进程组
#include <unistd.h>
pid_t getpgrp(void);//返回调用进程的进程组ID
#include <unistd.h>
pid_t getpid(pid_t pid);
成功返回进程组ID,失败返回-1
getpgid(0) == getpgrp()
调用setpgid可以将进程加入到一个现有的进程组或者创建一个新进程组。
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
成功返回0,出错返回-1
如果pid == pgid,则由pid指定的进程就变成进程组组长。
如果pid是0,则使用调用者的进程ID。
如果pgid是0,则pid指定的进程ID用作进程组ID。
进程只能为它自己或者它的子进程设置进程组ID。但是它的子进程调用了exec后,它就不再更改子进程的进程组ID。