Linux系统的启动过程为:加电自检-->根据BIOS中的设置从指定的设备启动-->找到设备MBR中的bootloader引导启动系统-->启动kernel-->启动init进程。
init进程根据/etc/inittab来在不同的运行级别启动相应的进程或执行相应的操作。
inittab文件中定义的登记项都是以:隔开的四个段,即:
id:runlevels:action:process
id:每个登记项的标识符,用于唯一标识每个登记项,不能重复
runlevels:系统的运行级别,表示process的action要在哪个级别下运行,该段中可以定义多个运行级别,各级别之间直接写不用分隔符。如果为空,表示在所有的运行级别运行。Linux的运行级别有:
0 | 表示关机 |
1 | 表示单用户模式,在这个模式中,用户登录不需要密码,默认网卡驱动是不被加载,一些服务不能用。 |
2 | 表示多用户模式,NFS服务不开启 |
3 | 表示命令行模式 |
4 | 这个模式保留未用 |
5 | 表示图形用户模式 |
6 | 表示重启系统 |
action:表示对应登记项的process在一定条件下所要执行的动作。
具体动作有:
respawn:当process终止后马上启动一个新的
wait:当进入指定的runlevels后process才会启动一次,并且到离开这个runlevels终止initdefault:设定默认的运行级别,即开机之后默认进入的运行级别,不能是0,6。
sysinit:系统初始化,只有系统开机或重新启动的时候,这个process才会被执行一次
powerwait:当init接收到电源失败信号的时候执行相应的process,并且如果init有进程在运行,会等待这个进程完成之后,再执行相应的process
powerfail:当init接收到电源失败信号的时候执行相应的process,并且如果init有进程在运行,不会等待这个进程完成,它会直接执行相应的process
powerokwait:电源已经故障,但是在等待执行对应操作的时候突然来电了就执行对应的process
powerfailnow:当电源故障并且init被通知UPS电源已经快耗尽执行相对应的process
ctrlaltdel:当用户按下ctrl+alt+del这个组合键的时候执行对应的process
boot:只有在引导过程中,才执行该进程,但不等待该进程的结束;当该进程死亡时,也不重新启动该进程
bootwait:只有在引导过程中,才执行该进程,并等待进程的结束;当该进程死亡时,也不重新启动该进程
off:如果process正在运行,那么就发出一个警告信号,等待20秒后,再通过杀死信号强行终止该process。如果process并不存在那么就忽略该登记项
once:启动相应的进程,但不等待该进程结束便继续处理/etc/inittab文件中的下一个登记项;当该进程死亡时,init也不重新启动该进程
process:表示启动哪个程序或脚本或执行哪个命令等
inittab文件如下:
id:3:initdefault
第一个字段为标识符。这个登记项表示默认的运行级别是3,也就是说默认开机启动会进入命令行模式。
si::sysinit:/etc/rc.d/rc.sysinit
所有的运行级别下,init依赖/etc/rc.d/rc.sysinit这个脚本对系统进行初始化。而/etc/rc.d/rc.sysinit的作用主要是:
1,激活udev和selinux
2,根据/etc/sysctl.conf文件设定内核参数
3,设定系统时钟
4,键盘的键映射,即识别键盘
5,启用swap分区
6,设定主机名
7,根文件系统检测及重新以读写方式挂载
8,激活RAID和LVM设备
9,启用磁盘配额
10,检测及挂载其他文件系统(挂载/etc/fstab中定义的设备)
11,清除过期的锁和PID文件
l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6
当进入n运行级(n就是上面所定义的运行级别),会把/etc/rc.d/rc*.d目录中所有以S开头的文件启用,把以K开头的文件给禁用,并且这些文件只在进入相应的运行级别时执行一次,退出此运行级别失效(因为action为wait)。所以每个运行级别的服务是独立的,而/etc/rc.d/rc*.d这个目录中存放的是在对应级别下所要开启和禁用的服务的文件。
ca::ctrlaltdel:/sbin/shutdown -t3 -r now
在所有运行级别下,按下ctrl+alt+del时等待3秒钟后重新启动系统。
pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"
在所有的运行级别下,如果电源故障,会给出提示信息,并且在2min后关机。pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"
在12345运行级别下,如果电源故障的时候,电源又恢复正常,则给出提示信息,并且立即终止关机。
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6
在2345运行级别下,会启用6个命令行界面的终端,并且一旦终端终止,就会在创建一个新的。
x:5:respawn:/etc/X11/prefdm -nodaemon
只有进入5运行级别,会打开图形用户终端,并且一旦终端终止,就会再创建一个新的。
可得inittab流程:
/etc/rc.d目录下有以下文件:
init.d rc rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d rc.local rc.sysinit
/etc/rc.d/init.d/
该文件夹包含所有服务在各个运行等级中的全部启动脚本。一般来说,它们都是标准的shell脚本,遵守最基本的标准。每个脚本最少接受两个参数start和stop,它们分别代表启动和停止服务(如网页服务)。除此之外,init脚本通常还会接受一些额外的选项,如restart(重启服务器)、status(返回服务当前状态)、reload(告知服务从配置文件中重新载入配置)以及force-reload(强制服务重载它的配置)。当用不带参数的方式运行脚本的时候,一般应该返回一个它会接受的参数列表。
init.d里面存放的是一些脚本,一般是linux以rpm包安装时设定的一些服务的启动脚本。系统在安装时装了好多rpm包,这里面就有很多对应的脚本。执行这些脚本可以用来启动,停止,重启这些服务。
/etc/rc.d/init.d这个目录下的脚本类似与windows中的注册表,在系统启动的时候执行。程序运行到这里(init进程读取了运行级别)。运行init.d里的脚本并不是直接运行,而是有选择的,因为系统并不需要启动所有的服务。
在决定了系统启动的run level之后,/etc/rc.d/rc这个脚本先执行。知道了运行级别之后,对于每一个运行级别,在rc.d下都有一个子目录分别是rc0.d,rc1.d ….. rc6.d。每个目录下都是到init.d目录的一部分脚本一些链接。每个级别要执行哪些服务就在相对应的目录下,比如级别5要启动的服务就都放在rc5.d下,但是放在这个rc5.d下的都是一些链接文件,链接到init.d中相对应的文件,真正执行任务的是init.d里的脚本。
/etc/rc.d/rc0.d/~/etc/rc.d/rc6.d/
这些文件夹分别包含每个运行等级对应的init脚本。在实际使用中,它们一般通过符号链接到/etc/init.d文件夹下的实际文件。当init进入一个运行等级的时候,它会按照数字顺序运行所有以K开头的脚本并传入stop参数,除非对应的init脚本在前一个运行等级中没有启动。然后init按照数字顺序运行所有以S开头的脚本并传入start参数。任何以D开头的init脚本都会被忽略—这让你可以在指定的运行等级禁止一个脚本,或者你也可以仅仅移除全部符号链接。所以如果有两个脚本,S01foo和S05bar,init首先会运行S01foo start,当它进入特定的运行等级后再执行S05bar start。
/etc/rc.d/rc.local
并非所有的发行版都使用了rc.local文件,通常它是一个留给用户修改的shell脚本。一般会在init进程结束的时候运行它,所以可以在这里放一些想要运行的额外脚本,而不用再创建自己的init脚本。
/etc/rc.d/rc.sysinit
rc.sysinit文件主要做在各个运行模式中相同的初始化工作,包括设定PATH、设定网络配置(/etc/sysconfig/network)、启动swap分区、设定/proc等等。
/etc/rc.d/rc
rc文件当运行级别改变时,负责启动/停止各种服务。
在Centos6.5中:
新建文件test.sh,输入以下两行
#!bin/bash表示使用bash解释器
赋予文件可执行权限,并且执行:
结果如下:
按照实验要求,更换输出信息为“Welcome! Have a nice day!”
接下来实现登录时自动运行脚本:
在登录Linux时要执行文件的过程如下:
在刚登录Linux时,首先启动 /etc/profile 文件,然后再启动用户目录下的 ~/.bash_profile、 ~/.bash_login或 ~/.profile文件中的其中一个,执行的顺序为:~/.bash_profile、 ~/.bash_login、 ~/.profile。
如果 ~/.bash_profile文件存在的话,一般还会执行 ~/.bashrc文件
在.bash_profile中有以下代码:
在.bashrc中有以下代码:
所以,~/.bashrc会调用 /etc/bashrc文件。最后,在退出shell时,还会执行 ~/.bash_logout文件。
执行顺序为: /etc/profile -> (~/.bash_profile | ~/.bash_login | ~/.profile) -> ~/.bashrc -> /etc/bashrc -> ~/.bash_logout
于是在.profile文件中加入这一行:
关于各个文件的作用域,在网上找到了以下说明:
(1) /etc/profile: 此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行. 并从/etc/profile.d目录的配置文件中搜集shell的设置。
(2) /etc/bashrc: 为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取(即每次新开一个终端,都会执行bashrc)。
(3) ~/.bash_profile: 每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次。默认情况下,设置一些环境变量,执行用户的.bashrc文件。
(4) ~/.bashrc: 该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该该文件被读取。
(5) ~/.bash_logout: 当每次退出系统(退出bash shell)时,执行该文件. 另外,/etc/profile中设定的变量(全局)的可以作用于任何用户,而~/.bashrc等中设定的变量(局部)只能继承 /etc/profile中的变量,他们是"父子"关系。
(6) ~/.bash_profile: 是交互式、login 方式进入 bash 运行的~/.bashrc 是交互式 non-login 方式进入 bash 运行的通常二者设置大致相同,所以通常前者会调用后者。
/etc/profile和/etc/environment等各种环境变量设置文件的用处
先将export LANG=zh_CN加入/etc/profile ,退出系统重新登录,登录提示显示英文。
将/etc/profile 中的export LANG=zh_CN删除,将LNAG=zh_CN加入/etc/environment,退出系统重新登录,登录提示显示中文。
用户环境建立的过程中总是先执行/etc/profile然后在读取/etc/environment。为什么会有如上所叙的不同呢?
应该是先执行/etc/environment,后执行/etc/profile。
/etc/environment是设置整个系统的环境,而/etc/profile是设置所有用户的环境,前者与登录用户无关,后者与登录用户有关。
登陆系统时shell读取的顺序应该是
/etc/profile ->/etc/enviroment -->$HOME/.profile -->$HOME/.env
找到.profile文件:
增加一行,实现登录时执行脚本test.sh
将ubuntu默认启动模式改为命令行启动
执行reboot后查看效果,可见脚本已经可以在登录后自动执行
修改脚本test.sh,添加输出信息
用户登录后,脚本test.sh自动运行,输出欢迎信息及相关系统信息。
修改命令提示符:
(4)编写一个daemon进程,该进程每隔10秒执行ps命令,并将当前时间和命令的输出写至文件ps.log尾部。
Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。
守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。
守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。
在本实验中使用库函数daemon()创建守护进程:
Daemon原型:
nochdir:当此参数为0时,会更改创建出的daemon的执行目录为根目录,否则(非0)时保持当前执行目录不变。
noclose:当参数为0时,会将标准输入(0),标准输出(1),标准错误(2)重定向到/dev/null,否则保持原有标准输入(0),标准输出(1),标准错误(2)不变。无论noclose 是否为0,daemon函数都不会关闭之前打开的大于等于3的fd。
此处为了测试方便,将循环次数设为3。
使用“>>”将输出重定向到ps.log,使用system执行系统命令。
可以看到所有信息可以正常打印到日志ps.log中
再将函数修改为daemon()进程,直接调用系统函数daemon(),参数意义同上
可以看到在ps.log文件中也可以正常打印