浅析 Linux 初始化 init 系统,第 2 部分: UpStart
Upstart 简介
假如您使用的 Linux 发行版是 Ubuntu,很可能会发现在您的计算机上找不到/etc/inittab 文件了,这是因为 Ubuntu 使用了一种被称为 upstart 的新型 init 系统。
相关系列文章:
- 浅析 Linux 初始化 init 系统,第 1 部分: sysvinit http://www.linuxidc.com/Linux/2014-12/110382.htm
- 浅析 Linux 初始化 init 系统,第 2 部分: UpStart http://www.linuxidc.com/Linux/2014-12/110381.htm
- 浅析 Linux 初始化 init 系统,第 3 部分: Systemd http://www.linuxidc.com/Linux/2014-12/110383.htm
开发 Upstart 的缘由
大约在 2006 年或者更早的时候, Ubuntu 开发人员试图将 Linux 安装在笔记本电脑上。在这期间技术人员发现经典的 sysvinit 存在一些问题:它不适合笔记本环境。这促使程序员 Scott James Remnant 着手开发 upstart。
当 Linux 内核进入 2.6 时代时,内核功能有了很多新的更新。新特性使得 Linux 不仅是一款优秀的服务器操作系统,也可以被用于桌面系统,甚至嵌入式设备。桌面系统或便携式设备的一个特点是经常重启,而且要频繁地使用硬件热插拔技术。在现代计算机系统中,硬件繁多、接口有限,人们并非将所有设备都始终连接在计算机上,比如 U 盘平时并不连接电脑,使用时才插入 USB 插口。因此,当系统上电启动时,一些外设可能并没有连接。而是在启动后当需要的时候才连接这些设备。在 2.6 内核支持下,一旦新外设连接到系统,内核便可以自动实时地发现它们,并初始化这些设备,进而使用它们。这为便携式设备用户提供了很大的灵活性。
可是这些特性为 sysvinit 带来了一些挑战。当系统初始化时,需要被初始化的设备并没有连接到系统上;比如打印机。为了管理打印任务,系统需要启动 CUPS 等服务,而如果打印机没有接入系统的情况下,启动这些服务就是一种浪费。Sysvinit 没有办法处理这类需求,它必须一次性把所有可能用到的服务都启动起来,即使打印机并没有连接到系统,CUPS 服务也必须启动。
还有网络共享盘的挂载问题。在/etc/fstab 中,可以指定系统自动挂载一个网络盘,比如 NFS,或者 iSCSI 设备。在本文的第一部分 sysvinit 的简介中可以看到,sysvinit 分析/etc/fstab 挂载文件系统这个步骤是在网络启动之前。可是如果网络没有启动,NFS 或者 iSCSI 都不可访问,当然也无法进行挂载操作。Sysvinit 采用 netdev 的方式来解决这个问题,即/etc/fstab 发现 netdev 属性挂载点的时候,不尝试挂载它,在网络初始化并使能之后,还有一个专门的 netfs 服务来挂载所有这些网络盘。这是一个不得已的补救方法,给管理员带来不便。部分新手管理员甚至从来也没有听说过 netdev 选项,因此经常成为系统管理的一个陷阱。
针对以上种种情况,Ubuntu 开发人员在评估了当时的几个可选 init 系统之后,决定重新设计和开发一个全新的 init 系统,即 UpStart。UpStart 基于事件机制,比如 U 盘插入 USB 接口后,udev 得到内核通知,发现该设备,这就是一个新的事件。UpStart 在感知到该事件之后触发相应的等待任务,比如处理/etc/fstab 中存在的挂载点。采用这种事件驱动的模式,upstart 完美地解决了即插即用设备带来的新问题。
此外,采用事件驱动机制也带来了一些其它有益的变化,比如加快了系统启动时间。sysvinit 运行时是同步阻塞的。一个脚本运行的时候,后续脚本必须等待。这意味着所有的初始化步骤都是串行执行的,而实际上很多服务彼此并不相关,完全可以并行启动,从而减小系统的启动时间。在 Linux 大量应用于服务器的时代,系统启动时间也许还不那么重要;然而对于桌面系统和便携式设备,启动时间的长短对用户体验影响很大。此外云计算等新的 Server 端技术也往往需要单个设备可以更加快速地启动。
UpStart 满足了这些需求,目前不仅桌面系统 Ubuntu 采用了 UpStart,甚至企业级服务器级的 RHEL 也默认采用 UpStart 来替换 sysvinit 作为 init 系统。
Upstart 的特点
UpStart 解决了之前提到的 sysvinit 的缺点。采用事件驱动模型,UpStart 可以:
- 更快地启动系统
- 当新硬件被发现时动态启动服务
- 硬件被拔除时动态停止服务
这些特点使得 UpStart 可以很好地应用在桌面或者便携式系统中,处理这些系统中的动态硬件插拔特性。
Upstart 概念和术语
Upstart 的基本概念和设计清晰明确。UpStart 主要的概念是 job 和 event。Job 就是一个工作单元,用来完成一件工作,比如启动一个后台服务,或者运行一个配置命令。每个 Job 都等待一个或多个事件,一旦事件发生,upstart 就触发该 job 完成相应的工作。
Job
Job 就是一个工作的单元,一个任务或者一个服务。可以理解为 sysvinit 中的一个服务脚本。有三种类型的工作:
- task job;
- service job;
- abstract job;
task job 代表在一定时间内会执行完毕的任务,比如删除一个文件;
service job 代表后台服务进程,比如 apache httpd。这里进程一般不会退出,一旦开始运行就成为一个后台精灵进程,由 init 进程管理,如果这类进程退出,由 init 进程重新启动,它们只能由 init 进程发送信号停止。它们的停止一般也是由于所依赖的停止事件而触发的,不过 upstart 也提供命令行工具,让管理人员手动停止某个服务;
Abstract job 仅由 upstart 内部使用,仅对理解 upstart 内部机理有所帮助。我们不用关心它。
除了以上的分类之外,还有另一种工作(Job)分类方法。Upstart 不仅可以用来为整个系统的初始化服务,也可以为每个用户会话(session)的初始化服务。系统的初始化任务就叫做 system job,比如挂载文件系统的任务就是一个 system job;用户会话的初始化服务就叫做 session job。
Job 生命周期
Upstart 为每个工作都维护一个生命周期。一般来说,工作有开始,运行和结束这几种状态。为了更精细地描述工作的变化,Upstart 还引入了一些其它的状态。比如开始就有开始之前(pre-start),即将开始(starting)和已经开始了(started)几种不同的状态,这样可以更加精确地描述工作的当前状态。
工作从某种初始状态开始,逐渐变化,或许要经历其它几种不同的状态,最终进入另外一种状态,形成一个状态机。在这个过程中,当工作的状态即将发生变化的时候,init 进程会发出相应的事件(event)。
表 1.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 | 任务即将被停止 |
post-stop | 执行 post-stop 段 |
图 1 展示了 Job 的状态机。
图 1. Job’s life cycle
其中有四个状态会引起 init 进程发送相应的事件,表明该工作的相应变化:
- Starting
- Started
- Stopping
- Stopped
而其它的状态变化不会发出事件。那么我们接下来就来看看事件的详细含义吧。