有两种人员需要了解 Upstart 的使用。第一类是系统开发人员,比如 MySQL 的开发人员。它们需要了解如何编写工作配置文件,以便用 UpStart 来管理服务。比如启动,停止 MySQL 服务。
另外一种情况是系统管理员,它们需要掌握 Upstart 的管理命令以便配置和管理系统的初始化,管理系统服务。
系统开发人员需要了解的 UpStart 知识系统开发人员不仅需要掌握工作配置文件的写法,还需要了解一些针对服务进程编程上的要求。本文仅列出了少数工作配置文件的语法。要全面掌握工作配置文件的写法,需要详细阅读 Upstart 的手册。这里让我们来分析一下如何用 Upstart 来实现传统的运行级别,进而了解如何灵活使用工作配置文件。
Upstart 系统中的运行级别
Upstart 的运作完全是基于工作和事件的。工作的状态变化和运行会引起事件,进而触发其它工作和事件。
而传统的 Linux 系统初始化是基于运行级别的,即 SysVInit。因为历史的原因,Linux 上的多数软件还是采用传统的 SysVInit 脚本启动方式,并没有为 UpStart 开发新的启动脚本,因此即便在 Debian 和 Ubuntu 系统上,还是必须模拟老的 SysVInit 的运行级别模式,以便和多数现有软件兼容。
虽然 Upstart 本身并没有运行级别的概念,但完全可以用 UpStart 的工作模拟出来。让我们完整地考察一下 UpStart 机制下的系统启动过程。
系统启动过程
下图描述了 UpStart 的启动过程。
图 4.UpStart 启动过程系统上电后运行 GRUB 载入内核。内核执行硬件初始化和内核自身初始化。在内核初始化的最后,内核将启动 pid 为 1 的 init 进程,即 UpStart 进程。
Upstart 进程在执行了一些自身的初始化工作后,立即发出"startup"事件。上图中用红色方框加红色箭头表示事件,可以在左上方看到"startup"事件。
所有依赖于"startup"事件的工作被触发,其中最重要的是 mountall。mountall 任务负责挂载系统中需要使用的文件系统,完成相应工作后,mountall 任务会发出以下事件:local-filesystem,virtual-filesystem,all-swaps,
其中 virtual-filesystem 事件触发 udev 任务开始工作。任务 udev 触发 upstart-udev-bridge 的工作。Upstart-udev-bridge 会发出 net-device-up IFACE=lo 事件,表示本地回环 IP 网络已经准备就绪。同时,任务 mountall 继续执行,最终会发出 filesystem 事件。
此时,任务 rc-sysinit 会被触发,因为 rc-sysinit 的 start on 条件如下:
start on filesystem and net-device-up IFACE=lo
任务 rc-sysinit 调用 telinit。Telinit 任务会发出 runlevel 事件,触发执行/etc/init/rc.conf。
rc.conf 执行/etc/rc$.d/目录下的所有脚本,和 SysVInit 非常类似,读者可以参考本文第一部分的描述。
程序开发时需要注意的事项
作为程序开发人员,在编写系统服务时,需要了解 UpStart 的一些特殊要求。只有符合这些要求的软件才可以被 UpStart 管理。
规则一,派生次数需声明。
很多 Linux 后台服务都通过派生两次的技巧将自己变成后台服务程序。如果您编写的服务也采用了这个技术,就必须通过文档或其它的某种方式明确地让 UpStart 的维护人员知道这一点,这将影响 UpStart 的 expect stanza,我们在前面已经详细介绍过这个 stanza 的含义。
规则二,派生后即可用。
后台程序在完成第二次派生的时候,必须保证服务已经可用。因为 UpStart 通过派生计数来决定服务是否处于就绪状态。
规则三,遵守 SIGHUP 的要求。
UpStart 会给精灵进程发送 SIGHUP 信号,此时,UpStart 希望该精灵进程做以下这些响应工作:
•完成所有必要的重新初始化工作,比如重新读取配置文件。这是因为 UpStart 的命令"initctl reload"被设计为可以让服务在不重启的情况下更新配置。
•精灵进程必须继续使用现有的 PID,即收到 SIGHUP 时不能调用 fork。如果服务必须在这里调用 fork,则等同于派生两次,参考上面的规则一的处理。这个规则保证了 UpStart 可以继续使用 PID 管理本服务。
规则四,收到 SIGTEM 即 shutdown。
•当收到 SIGTERM 信号后,UpStart 希望精灵进程进程立即干净地退出,释放所有资源。如果一个进程在收到 SIGTERM 信号后不退出,Upstart 将对其发送 SIGKILL 信号。
系统管理员需要了解的 Upstart 命令作为系统管理员,一个重要的职责就是管理系统服务。比如系统服务的监控,启动,停止和配置。UpStart 提供了一系列的命令来完成这些工作。其中的核心是initctl,这是一个带子命令风格的命令行工具。
比如可以用 initctl list 来查看所有工作的概况:
$initctl list
alsa-mixer-save stop/waiting
avahi-daemon start/running, process 690
mountall-net stop/waiting
rc stop/waiting
rsyslog start/running, process 482
screen-cleanup stop/waiting
tty4 start/running, process 859
udev start/running, process 334
upstart-udev-bridge start/running, process 304
ureadahead-other stop/waiting
这是在 Ubuntu10.10 系统上的输出,其它的 Linux 发行版上的输出会有所不同。第一列是工作名,比如 rsyslog。第二列是工作的目标;第三列是工作的状态。
此外还可以用 initctl stop 停止一个正在运行的工作;用 initctl start 开始一个工作;还可以用 initctl status 来查看一个工作的状态;initctl restart 重启一个工作;initctl reload 可以让一个正在运行的服务重新载入配置文件。这些命令和传统的 service 命令十分相似。
表 2.service 命令和 initctl 命令对照表 Service 命令UpStart initctl 命令service start initctl start
service stop initctl stop
service restart initctl restart
service reload initctl reload
很多情况下管理员并不喜欢子命令风格,因为需要手动键入的字符太多。UpStart 还提供了一些快捷命令来简化 initctl,实际上这些命令只是在内部调用相应的 initctl 命令。比如 reload,restart,start,stop 等等。启动一个服务可以简单地调用
start <job>
这和执行 initctl start <job>是一样的效果。
一些命令是为了兼容其它系统(主要是 sysvinit),比如显示 runlevel 用/sbin/runlevel 命令:
$runlevel
N 2
这个输出说明当前系统的运行级别为 2。而且系统没有之前的运行级别,也就是说在系统上电启动进入预定运行级别之后没有再修改过运行级别。
那么如何修改系统上电之后的默认运行级别呢?
在 Upstart 系统中,需要修改/etc/init/rc-sysinti.conf 中的 DEFAULT_RUNLEVEL 这个参数,以便修改默认启动运行级别。这一点和 sysvinit 的习惯有所不同,大家需要格外留意。
还有一些随 UpStart 发布的小工具,用来帮助开发 UpStart 或者诊断 UpStart 的问题。比如 init-checkconf 和 upstart-monitor
还可以使用 initctl 的 emit 命令从命令行发送一个事件。
#initctl emit <event>
这一般是用于 UpStart 本身的排错。
Upstart 小结可以看到,UpStart 的设计比 SysVInit 更加先进。多数 Linux 发行版上已经不再使用 SysVInit,一部分发行版采用了 UpStart,比如 Ubuntu;而另外一些比如 Fedora,采用了一种被称为 systemd 的 init 系统。Systemd 出现的比 UpStart 更晚,但发展迅速,虽然 UpStart 也还在积极开发并被越来越多地应用,但 systemd 似乎发展更快,我将在下一篇文章中再介绍 systemd。