对于Linux的启动过程,之前一直都是研究到内核运行/sbin/init,启动第一个用户进程为止,因为这部分一直都是在内核态工作,所以对于学习内核还是有帮助的,当时/sbin/init之后的过程也需要明白,今天就总结一下。
首先大体写一下内核从启动到执行/sbin/init的流程:
head.S ===> start_kernel ===> rest_init ===> cpu_idle和内核线程init ===》init_post ===> /sbin/init
而init执行后的大体流程是:
init ===> fork出 getty 按照/etc/inittab 打开相应终端 ===》exec 执行 login验证用户和密码 根据/etc/passwd ===》密码正确,设置一些环境变量 设置主目录 exec执行bash
getty login bash 是同一个进程
在工作中之前碰到过一个问题就是客户机器正常进入控制台后sudo su不能获取超级用户权限,说not in sudoer。
sudo su是使用普通用户密码来获取超级用户权限,但是并不是所有的普通用户都可以用sudo su来获取超级用户权限,具体的授权在/etc/sudoers中。也就是说我们在获取超级用户权限的时候用sudo su,这时系统回去sudoers中查看该申请用户是否在列表中,如果在则该用户获取超级用户权限,此后一段时间该用户则是超级用户权限,时间长短也是在/etc/sudoers中定义的
这样分析也就是说客户机器上的这个用户没有在sudoers列表中,可以用adduser命令来添加到列表中,但是添加sudoers是需要超级用户权限的。。。这就形成了一个死循环!
想到的解决办法是在内核启动是参数init=/bin/bash,这样内核启动后不是执行init,而是跳过init gettty login直接执行bash,这样的话没有执行login,系统也就没有启用任何一个用户,这是的控制台就是超级用户权限,这是使用命令adduser将用户添加到sudoer中就可以了,这样该普通用户就可以获取超级用户权限了
在登录到Linux系统中之后,你会发现(使用”top”或”ps –ax”命令)自己终端原来的getty进程已经找不到了。因为getty进程执行了login程序,被替换成了login进程,并且最后被替换成你的登录shell进程。
当你在”login: “提示符下键入了你的用户名后,getty会读取用户名并且去执行login程序,也把用户名信息传给了它。因此getty进程被替换成了login进程。此时login进程会接着要求你输入口令。在口令检查通过后就会去执行/etc/passwd文件中对应你用户名项中记录的程序。通常这个程序是bash shell程序。因此原来的getty进程最终被替换成了bash进程,对应的这三个程序也就都具有相同的进程ID。
当注销登录(log out)时,则该终端上的所有进程都会被终止(killed),包括登录shell进程bash。因此,对于在/etc/inittab文件中列出的getty程序,一旦其被替换执行的bash程序被终止或退出,init进程就会为对应终端重新创建一个getty进程。
login程序则主要用于要求登录用户输入密码。根据用户输入的用户名,它从口令文件passwd中取得对应用户的登录项,然后调用getpass()以显示”password:”提示信息,读取用户键入的密码,然后使用加密算法对键入的密码进行加密处理,并与口令文件中该用户项中pw_passwd字段作比较。如果用户几次键入的密码均无效,则login程序会以出错码1退出执行,表示此次登录过程失败。此时父进程(进程init)的wait()会返回该退出进程的pid,因此会根据记录下来的信息再次创建一个子进程,并在该子进程中针对该终端设备再次执行agetty程序,重复上述过程。
login程序也可以被用户在运行过程中在shell下当作一个命令执行。此时它可以被用随时从一个用户切换成另一个用户。如果执行时没有给出参数,则login就会显示输入用户名的提示信息。如果用户不是超级用户(root),并且/etc/目录下存在一个名为nologin的文件,那么该文件中的信息就会被显示出来,此次登录过程也随即被终止。
如果在/etc/usertty文件中对该用户指定了特殊的访问限制,那么这些限制要求必须满足。如果是一个超级用户,那么所使用的登录tty设备必须是在/etc/securetty文件中指定的。
在所有这些条件满足之后,login同样也会要求用户输入密码并对其进行检查。如果.hushlogin存在的话,login就会执行一个“安静”的登录过程,也即不检查是否有邮件,也不显示上次登录时间和motd文件中的信息。否则如果/var/log/lastlog文件存在的话,就会显示其中的最后登录时间。
如果用户键入的密码正确,则login就会把当前工作目录(Currend Work Directory)修改成口令文件中指定的该用户的起始工作目录。并把对该终端设备的访问权限修改成用户读/写和组写,设置进程的组ID。然后利用所得到的信息初始化环境变量信息,例如起始目录(HOME=)、使用的shell程序(SHELL=)、用户名(USER=和LOGNAME=)和系统执行程序的默认路径序列(PATH=)。接着显示/etc/motd文件(message-of-the-day)中的文本信息,并检查并显示该用户是否有邮件的信息。最后login程序改变成登录用户的用户ID并执行口令文件中该用户项中指定的shell程序,如bash或csh等。
如果口令文件/etc/passwd中该用户项中没有指定使用哪个shell程序,系统则会使用默认的/bin/sh程序。如果口令文件中也没有为该用户指定用户起始目录的话,系统就会使用默认的根目录/。有关login程序的一些执行选项和特殊访问限制的说明,请参见Linux系统中的在线手册页(man 8 login)。
Shell程序是一个复杂的命令行解释程序,是当用户登录系统进行交互操作时执行的程序。它是用户与计算机进行交互操作的地方。它获取用户输入的信息,然后执行命令。用户可以在终端上向shell直接进行交互输入,也可以使用shell脚本文件向shell解释程序输入。在Linux系统中,目前常用的shell有:
Bourne Again Shell,/bin/bash
C shell,/bin/csh(或tcsh)
BSD shell/bin/ash(或bsh)
在登录过程中,系统(login)会从口令文件用户对应登录项的最后一个字段知道应该为用户执行哪个shell程序。
shell程序中实现了一个具有流控制结构的语言,使用相当广泛。目前这些shell程序都朝着与IEEE POSIX 1003.2兼容的方向发展,因此它们各自虽然各自有自己的特点,但基本功能已经越来越相象。本书主要介绍bash的工作原理和实现机制,其它几种shell的实现机制与之类似。
在登录过程中login开始执行shell时,所带参数argv[0]的第一个字符是’-’,表示该shell是作为一个登录shell被执行。此时该shell程序会根据该字符,执行某些与登录过程相应的操作。登录shell会首先从/etc/profile文件以及.profile文件(若存在的话)读取命令并执行。如果在进入shell时设置了ENV环境变量,或者在登录shell的.profile文件中设置了该变量,则shell下一步会从该变量命名的文件中读去命令并执行。因此用户应该把每次登录时都要执行的命令放在.profile文件中,而把每次运行shell都要执行的命令放在ENV变量指定的文件中。设置ENV环境变量的方法是把下列语句放在你起始目录的.profile文件中。
ENV=$HOME/.anyfilename; export ENV
在执行shell时,除了一些指定的可选项以外,如果还指定了命令行参数,则shell会把第一个参数看作是一个脚本文件名并执行其中的命令,而其余的参数则被看作是shell的位置参数($1、$2等)。否则shell程序将从其标准输入中读取命令。
在执行shell程序时可以有很多选项,请参见Linux系统中的有关sh的在线手册页中的说明。