再一次讲到Linux系统启动流程:
POST --> Boot Sequence --> Bootloader(grub) --> kernel + initramfs(initrd) --> rootfs --> /sbin/init
对于init,即系统内核加载完毕后(加载kernel和切换根文件系统)运行的第一个用户空间进程,是管理用户空间的首进程,其他用户进程都是直接或间接由此进程创建的。init 进程以守护进程的方式存在,负责组织与运行系统的相关初始化工作,让系统进入定义好的运行模式。对于不同的CentOS版本,init也不同:
CentOS 5:SysV init
CentOS 6:Upstart
CentOS 7:systemd
CentOS5和6的init虽然不同,但进程名都是init,CentOS7则改成了systemd
对于SysVinit,执行的流程是:
内核加载完毕后,执行第一个进程init,即/sbin/init, PID 为1。
启动init程序后,init进程首先读取/etc/inittab文件,分析文件内容,获得以下的配置信息:
▲ 系统需要进入的运行级别(runlevel)
▲ 捕获组合键的定义
▲ 定义电源fail/restore脚本
▲ 启动getty和虚拟控制台
获取系统运行级别之后,根据运行级别顺序的执行以下位置的启动脚本,从而将系统初始化为预设的运行级别:
▲ /etc/rc.d/rc.sysinit 重要的系统初始化服务
▲ /etc/rc.d/rc 和 /etc/rc.d/rcX.d ,X为运行级别
▲ /etc/rc.d/rc.local 用户个性化服务
这样,整个系统就启动起来了。
/etc/rc.d/rc.sysinit执行一些重要的系统初始化任务:
- 激活 udev 和 selinux
- 设置定义在/etc/sysctl.conf 中的内核参数
- 设置系统时钟
- 加载 keymaps
- 使能交换分区
- 设置主机名(hostname)
- 根分区检查和 remount
- 激活 RAID 和 LVM 设备
- 开启磁盘配额
- 检查并挂载所有文件系统
- 清除过期的 locks 和 PID 文件
/etc/rc.d/rc脚本文件,就是确定当前运行级别,然后遍历/etc/rc.d/rcX.d/目录下的文件(X是运行级别,如3级别,就是rc3.d),这些文件名字是有规律的,都是以K或S开头,后跟两位数字,在后面是脚本名字,并且,这些文件都是软链接,链接到/etc/init.d/下对应的脚本文件。如下:
K##*,##运行次序,越小越先运行,K是Kill要关闭的,数字越小的服务,通常为依赖到别的服务。
S##*,S是Start,要启动的,数字越小越先运行,数字小的服务,通常为被依赖到的服务。
为什么要使用软链接?因为rc0.d到rc6.d中都可能要运行这些脚本,每个目录下有一份拷贝,维护和存储都有问题,使用软链接,统一且空间占用少。
SysVinit是串行启动,即前一个脚本运行完,才能运行后面的,而不管之间是否存在依赖关系,速度慢,一个进程启动失败,容易导致其后所有进程无法启动,对于如文件系统挂载等不能很好的处理,如usb即插即用支持不好。
对于Upstart:
针对SysVinit的缺点,Ubuntu 开发人员重新设计和开发一个全新的 init 系统,即 UpStart。UpStart 基于事件机制,比如 U 盘插入 USB 接口后,udev 得到内核通知,发现该设备,这就是一个新的事件。UpStart 在感知到该事件之后触发相应的等待任务,比如处理/etc/fstab 中存在的挂载点。采用这种事件驱动的模式,upstart 完美地解决了即插即用设备带来的新问题。
此外,采用事件驱动机制也带来了一些其它有益的变化,比如加快了系统启动时间。sysvinit 运行时是同步阻塞的。一个脚本运行的时候,后续脚本必须等待。这意味着所有的初始化步骤都是串行执行的,而实际上很多服务彼此并不相关,完全可以并行启动,从而减小系统的启动时间。
Upstart 的特点:UpStart 解决了之前提到的 sysvinit 的缺点。采用事件驱动模型,UpStart 可以:
更快地启动系统
当新硬件被发现时动态启动服务(PnP,英文 Plug-and-Play,译文为即插即用)
硬件被拔除时动态停止服务
Upstart 概念和术语
UpStart 主要的概念是 job 和 event。
(一)Job
Job是一个工作的单元(unit),一个任务(task)或者一个服务(service)。每个 Job 都等待一个或多个事件,一旦事件发生,UpStart 就触发该 Job 完成相应的工作。可以理解为 sysvinit 中的一个服务脚本。有三种类型的工作:
● task job;
● service job;
● abstract job;
task job 代表在一定时间内会执行完毕的任务,比如删除一个文件;
service job 代表后台服务进程,比如 apache httpd。这里进程一般不会退出,一旦开始运行就成为一个后台精灵进程,由 init 进程管理,如果这类进程退出,由 init 进程重新启动,它们只能由 init 进程发送信号停止。它们的停止一般也是由于所依赖的停止事件而触发的,不过 upstart 也提供命令行工具,让管理人员手动停止某个服务;
Abstract job 仅由 upstart 内部使用。
init daemon会监测每个服务的状态,如果服务出现问题会重启服务,在某些事件触发时或手工停止时会杀死服务。
Upstart init daemon只能监测那些使用exec运行的作业,无法监测使用script…end script运行的作业。也就是说,服务应该使用exec运行,而任务则可以使用任意的方法。
Upstart init守护进程读取/etc/init目录下的作业配置文件,并使用inotify来监控它们的改变。配置文件名必须以.conf结尾,可以放在/etc/init/下的子目录中。每个文件定义一个服务或作业,其名称按路径名来称呼。例如定义在/etc/init/rc-sysinit.conf中的作业就称为rc-sysinit,而定义在/etc/init/net/apache.conf的作业称为net/apache。这些文件必须是纯文本且不可执行的。
进程(Process): Process是由工作(jobs)定义的服务(Services)或者任务(Task),它将被init daemon运行。每个job可以定义一个或者多个不同的process,分别在其生命周期的不同状态运行。除抽象作业(Abstact Job)外的所有作业配置文件都必须要含有exec节(exec stanza)或者script节(script stanza)。它们指定这个工作运行什么文件。
除了以上的分类之外,还有另一种工作(Job)分类方法。Upstart 不仅可以用来为整个系统的初始化服务,也可以为每个用户会话(session)的初始化服务。系统的初始化任务就叫做 system job,比如挂载文件系统的任务就是一个 system job;用户会话的初始化服务就叫做 session job。
Job 生命周期
Upstart 为每个工作都维护一个生命周期。一般来说,工作有开始,运行和结束这几种状态。为了更精细地描述工作的变化,Upstart 还引入了一些其它的状态。比如开始就有开始之前(pre-start),即将开始(starting)和已经开始了(started)几种不同的状态,这样可以更加精确地描述工作的当前状态。
工作从某种初始状态开始,逐渐变化,或许要经历其它几种不同的状态,最终进入另外一种状态,形成一个状态机。在这个过程中,当工作的状态即将发生变化的时候,init 进程会发出相应的事件(event)。
Upstart 中 Job 的可能状态
状态名 : 含义
Waiting : 初始状态
Starting Job : 即将开始
pre-start : 执行 pre-start 段,即任务开始前应该完成的工作
Spawned : 准备执行 script 或者 exec 段
post-start : 执行 post-start 动作
Running : interim state set after post-start section processed denoting job is running (But it may have no associated PID!)
pre-stop : 执行 pre-stop 段
Stopping : interim state set after pre-stop section processed
Killed : 任务即将被停止