Upstart是一个用于替代传统 init 的系统初始化程序。相对于 init 的同步执行,Upstart 是事件驱动、异步工作的。由于是事件驱动, Upstart 提供了传统 init 没法提供的功能,如机器运行时添加或删除U盘;由于异步工作,Upstart 更能充分利用CPU资源,性能更好。
Ubuntu 是我们常用的开发和服务器系统,当前最新 LTS 使用的是 Upstart,了解 Upstart 可以帮我们解答以下这些疑惑:
- 机器启动后的一些初始化脚本应该放在哪里比较合适?
- 想要在某个通用服务启动前执行一段代码可以怎样做?
- 设置好开机启动的程序运行时网络是否已经配置好?
- 硬盘什么时候挂载的?
- 在一些重启后某些硬盘内容会消失的云主机里什么时候初始化 flashcache?
下面介绍一下日常最需要了解 Upstart 的一些方面:
一、基本概念
Upstart 的可执行文件就是系统里的 /sbin/init。它事件驱动的特性体现在通过一些事件来触发任务的执行,比如开机后有 startup 事件、启动一个 job 前有 starting xxx(job 的名字)事件、插入一个U盘也会有相应的事件。Upstart 的任务配置一般写在 /etc/init 目录下(其实还有用户的 Upstart 配置,可以放在 $HOME/.init 目录下)。
Upstart job 的三个类型:
- Task Job: 运行一小会儿就停止的服务。
- Service Job: 一直运行的服务,比如 PHP-FPM、sshd。
- Abstract Job:没有 exec 和 script 的任务, Upstart 不会跟踪他的pid, 常用来设置网络服务。
二、Upstart 在机器启动时的执行过程
在机器做完那些加载内核,挂载跟目录等工作后,操作系统会调用 /sbin/init 来接管后续的服务启动过程。
- init 启动后马上发出第一个事件 startup
hostname,mountall 会被 startup 事件触发(在 /etc/init 目录下 grep startup * 就可以看到了!),也就是一启动就开始分别设置 hostname 和 挂载硬盘。
在 mountall.conf 里可以看到它主要运行了 mountall 程序,这个程序会读取 /lib/init/fstab 和 /etc/fstab 里的配置,按顺序挂载。其中 /lib/init/fstab 里的都是像 /proc /sys 这样的虚拟磁盘。
mountall 会发出很多可以触发 Upstart 的事件:
emits virtual-filesystems emits local-filesystems emits remote-filesystems emits all-swaps emits filesystem emits mounting emits mounted
每挂载好一个磁盘都会发出一个 mounted 事件,当挂载完 /lib/init/fstab 里的虚拟磁盘后会发出 virtual-filesystems 事件,mountall 继续处理 /etc/fstab 里的配置,期间会发出 remote-filesystems, all-swaps等事件,全部处理完后会发送 local-filesystems 和 filesystem 事件。
也就是说,当 Upstart 接收到 filesystem 事件时,配置文件中的磁盘已经挂载好了。
mountall 发出的很多事件,有些是以阻塞的方式发出的,也就是在 mountall 过程中有些别的任务已经被触发并且可能已经完成了。
udev.conf 在接收到 virtual-filesystem 时出发,这是一个管理 /dev 下的设备描述的程序。
- udev 导致 upstart-udev-bridge 任务执行,这个任务会设置好 127.0.0.0 这个网络回路地址。
- udev 和 filesystem 事件还是开始网络设置的前提。
- 在设置网络之前,ufw 的配置也会准备就绪。
- 网络和文件系统都准备好后出发 rc-sysinit.conf 里的脚本执行,里面其实就是打开 telinit 程序并传入一个2作为参数,这个程序发出 runlevel 事件。
- rc.conf 会被 runlevel 事件触发,它会调用 /etc/init.d/rc 2 , 这是兼容传统 init 的关键步骤。
- 其他 job 也会被 runlevel 事件触发,例如 cron、ssh、irqbalance 等。
三、 和传统 init 的兼容
Upstart 兼容传统的 init 也称为 sysV 的启动方式,从上面步骤可以看到在 /etc/init/rc.conf 里调用了 /etc/init.d/rc 2, 这个程序会遍历 /etc/rc2.d 目录下的脚本并执行。
平时我们启动服务会用到下面这几种命令:
service x start
start x
initctl start x
其中 service 是一个脚本,它的工作原理是先检查 /etc/init 里的配置有没有这个命令要启动的服务,如果没有才到 /etc/init.d 里找到启动那个服务的脚本并执行。如果你通过 apt-get 安装 PHP-FPM 可以发现 /etc/init.d 里的 PHP-FPM 启动脚本并不会被 service php5-fpm start 调用。