首先,每个进程都有一个父进程(初始化内核进程通常是自己的父进程);
当子进程终止时会通知父进程, 从而父进程可以获取子进程的退出状态.
系统管理员创建一个文件, 通常叫/etc/ttys, 每行一个终端设备.每行都包含了设备的名字和传给getty的参数(比如波特率).当系统引导时,内核创建进程ID 1, 即init进程. 它把系统带到多用户状态.
init进程读取/etc/ttys, 并对每个终端设备都允许一个登录, 通过getty的exec进行一个fork, 如上图所示.
getty(包括getty)之上的进程,其real user ID和effective user ID都是0.
getty负责对终端设备调用open. 一旦设备呗打开, 文件描述符0, 1, 2就相应地设置了. 同时getty输出像login: 这样的字符,等待用户输入用户名. 如果终端支持多种速率, getty可以通过侦测特殊字符来告诉终端改变波特率.
当我们输入用户名,getty的工作就完成了,它将会触发login程序(默认为login, 可在gettytab文件中修改), 有点像:
execle("/bin/login", "login", "-p", username, (char *)0, envp);
init所触发的getty是空环境的,所以getty必须为login创建一个带有终端名字的环境(envp变量), 其环境参数都列举在gettytab文件中. "-p"用于告诉login保存被传递的环境参数,增加上去, 而不是取代.
因为exec不会改变进程ID, 所以除了原始的init,其他的进程都有一个ID为1的父进程
login程序在有用用户名之后, 它能告诉getpwnam来获取密码文件的入口. 然后, login调用getpass来显示Password: 同时读取密码(当然得关闭echoing). 然后login调用crypt来加密密码并把加密的结果与shadow密码文件的pw_passwd域像比较; 若发生错误, login调用exit(通过getty的exec做一个fork), 重启程序.
以上所说是传统的UNIX系统认证过程, 现代的UNIX系统已经演化到支持多认证过程. 比如FreeBSD, Linux, Mac OS X, 和Solaris都支持PAM(pluggable Authentication Module). 管理员通过使用PAM库来对访问的服务配置相应的认证方法.
如果登录成功,login将会:
- 转到我们的home目录(chdir)
- 把终端的所有权改成用户的(chown)
- 改变终端的访问权限,以使用户可以读写
- 通过setgid和initgroup来设置用户的group IDs
- 用login所有的信息初始化环境: HOME, SHELL, USER LOGNAME, PATH
- 改变我们的user ID(通过setuid), 同时触发登录的shell------execl("/bin/sh", "-sh", (char *)0);
当然login还做了很多其他的工作, 比如打印message-of-the-day文件, 检查新邮件等等.
shell的父进程是最初的init(进程ID为1). 所以当shell关闭时, init会收到一个SIGCHLD的信号.
然后shell读取启动文件.profile .bash_profile. 这些启动文件通常会改变一些环境变量并增加一些环境变量.
最终我们得到了shell的提示符, 从而能够输出命令
LINUX与之不同的是/etc/inittab包含了init启动一个getty进程所需的配置信息,跟SYSTEM V类似. 根据不同版本的getty, 终端特性由命令行(用agetty时)指定或者由文件/etc/gettydefs指定(用mgetty时).