LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 8805|回复: 7

ubuntu中upstart进行系统初始化的原理

[复制链接]
发表于 2008-6-21 10:08:55 | 显示全部楼层 |阅读模式
ubuntu中upstart 替代 inittab 引导

http://hi.baidu.com/janux/blog/item/c607d2c840d24e167e3e6f47.html


并行启动应用程序从而加速 Linux 的引导

使用 initng 和 upstart
        developerWorks


M. Tim Jones (mtj@mtjones.com), 顾问工程师, Emulex

2007 年 3 月 26 日

    对 于 Linux® 的最大抱怨(尤其来自开发人员)就是 Linux 的引导速度。默认情况下,Linux 是一个通用的操作系统,可以作为一个开箱即用的客户机桌面或服务器使用。由于 Linux 具有很好的灵活性,因此可以用作各种用途的基础,但对于任何特定的配置来说,尚不是最理想的选择。本文将向您展示可以提高 Linux 引导速度的一些方法,包括两种可以对初始化过程进行并行化操作的方法。本文还介绍了如何图形化地表现引导过程的性能。

对于 GNU/Linux 常见的抱怨之一(除了缺乏良好的内核调试器之外)就是操作系统的启动需要花费大量时间。您可以将这个过程概括为引导,不过实际上已经有几个独立的项目开始从冷系统改进到可以通过 shell 或窗口管理器与系统进行交互。让我们来回顾一下 Linux 的引导和初始化过程。

Linux 引导的主要阶段

尽管引导 Linux 会涉及很多步骤,不过您可以将整个过程划分为 3 个基本步骤,我将它们分别称之为 BIOS、内核引导 和 系统初始化,如图 1 所示。

图 1. Linux 引导过程的短暂视图
Linux 引导过程

BIOS

当第一次启动计算机或重启时,计算机的处理器会在一个众所周知的位置开始执行,即基本输入/输出系统(BIOS)。 BIOS 通常存储在系统主板的一个闪存设备中。BIOS 需要执行很多工作,例如对基本组件(例如系统内存)进行初始测试,确定如何引导操作系统。由于基于 PC 的计算机都非常灵活,因此引导设备可以是连接到主板上的各种设备之一,包括硬盘、CD-ROM 或其他设备,例如网络接口。

通过选择最经常引导的设备(通常是硬盘)可以优化确定引导设备的过程。不过到目前为止,BIOS 阶段最耗时的过程就是内存测试。将这个测试的某些部分禁用(例如内存完全测试)确实有助于加快引导速度,不过这样做的代价是丢失了引导时对系统的完整性测试。

内核引导

找到引导设备后,开始 Linux 内核引导过程。这个过程发生在两个阶段(大约) —— 第一阶段引导 和第二阶段引导。第一阶段包含了一个简单的引导加载程序(这可以在引导设备的主引导记录 MBR 中找到),其作用是加载第二阶段的引导加载程序。第一阶段的引导加载程序使用分区表来找到第二阶段的引导加载程序。第一阶段的引导加载程序对表进行扫描,查找活动分区;当加载程序找到分区时,就将第二阶段的引导加载程序加载到 RAM 中并调用它。

在第二阶段的引导加载程序加载到 RAM 中之后,Linux 内核映像和初始 RAM 磁盘映像(initrd)也会被加载到 RAM 中。当调用内核时,它会自解压到高端内存中,并拷贝 initrd 以供稍后安装和使用。
       
LILO 和 GRUB

第一阶段的引导加载程序和第二阶段的引导加载程序通常被称为 LInux LOader (LILO)或 GRand Unified Bootloader(GRUB),这取决于您的系统使用的名称。

内核引导过程通常相当复杂,不过却非常迅速,因为大部分代码都是使用系统的机器语言编写的。在内核引导序列的结尾,会启动 init 进程。由于 init 是 Linux 系统中创建的第一个进程,因此它是所有其他进程的祖先(所有的进程都是 init 的后代)。

系统初始化

init 进程 —— 本文的重点 —— 是内核引导过程完成时创建的第一个进程。Linux 使用了 init 进程来对组成 Linux 的服务和应用程序进行初始化。

当 init 进程启动时,它会打开一个名为 /etc/inittab 的文件。这个文件是 init 的配置文件,定义了如何对系统进行初始化。这个文件还包含了有关出现电源故障时执行的操作(如果系统支持)、以及在检测到 Ctrl-Alt-Delete 键序列时应该如何反应的信息。请参看 清单 1 中该文件的简短片段,了解它所提供的内容。

inittab 配置文件使用通用格式定义了几项内容:id:runlevels:action:process。其中 id 是惟一标识该项的字符序列。runlevels 定义了操作所使用的运行级别。action 指定了要执行的特定操作。最后,process 定义了要执行的进程。

清单 1. inittab 文件摘录
  1. # The default runlevel
  2. id:2:initdefault
  3. # Boot-time system configuration/initialization script
  4. si::sysinit:/etc/init.d/rcS
  5. # Runlevels
  6. l0:0:wait:/etc/init.d/rc 0
  7. l1:1:wait:/etc/init.d/rc 1
  8. l2:2:wait:/etc/init.d/rc 2
  9. l3:3:wait:/etc/init.d/rc 3
  10. l4:4:wait:/etc/init.d/rc 4
  11. l5:5:wait:/etc/init.d/rc 5
  12. l6:6:wait:/etc/init.d/rc 6
  13. z6:6:respawn:/sbin/sulogin
  14. # How to react to ctrl-alt-del
  15. ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now
复制代码
       
Init 和 telinit

您可以使用 telinit 工具(这是一个指向 init 工具的链接)与 init 进程进行通信。例如,如果目前在多用户模式下(runlevel 2),希望切换到单用户模式(runlevel 1),使用命令 telinit 1 即可(使用超级用户模式)。

在 init 加载 /etc/inittab 之后,就会将系统切换到 initdefault 操作所定义的运行级别。如 清单 1 所示,即运行级别 2。我们可以将运行级别看作是系统的状态。例如,运行级别 0 定义了系统挂起状态,运行级别 1 是单用户模式。运行级别 2 到 5 是多用户状态,运行级别 6 表示重启。(注意有些发行版对于运行级别的表示是不同的)。还可以以另一种方式考虑运行级别,即它是一种定义可以执行哪些进程(定义系统状态的进程)的方法。

注意: 要查看系统的当前运行级别,请使用命令 runlevel。

正如 清单 1 定义的一样, initdefault 指定默认的 init 级别是 2 (多用户模式)。在定义初始的运行级别之后,则调用 rc 脚本以及参数 2(运行级别)来启动系统。这个脚本然后会调用各种服务和应用程序脚本来启动或停止特定的元素。在本例中,文件都是在 /etc/rc2.d/ 中定义的。例如,如果要启动 MySQL 应用程序(例如系统启动),可以这样调用:/etc/rc2.d/S20mysql start。在关闭系统时,则使用 stop 参数调用相同的脚本集。
       
修改 init 进程

修改初始化进程非常的简单。在引导时(使用 LILO 或 GRUB),指定一个新进程来开始处理系统初始化。指定 init=/sbin/mynewinit 作为内核引导行的一部分从而调用这个进程,而不是默认的 init 进程。在 ./linux/init/main.c 的内核源代码中您可以看到这种用法。如果在内核引导行中提供了一个 init 命令,引导时就会使用它。否则,内核就会尝试启动 4 个备选方法之一(第一个是 /sbin/init)。

最后,串行执行大量的脚本以启动各种需要的服务(通常可以在 Linux 的引导屏幕中看到)。即使这些服务彼此无关时,依然会顺次启动它们。其结果是引导过程非常耗时(尤其在具有很多服务的大型系统上更是如此)。

关于这个问题的一个很明显的解决方案是去掉 init 命令的串行特性,将其替换成并行化操作。在很多多处理系统中都可以看到这种用法。例如,socket striping,或者使用两个或多个 socket 并行地移动数据,就是一个基于这个主题的解决方案。独立磁盘冗余阵列(RAID)系统也是通过将磁盘分成条状(通常是并行的)来提高 I/O 性能。





Init 守护进程的替换
       
简单的 init 优化

对 init 进程进行优化的最简单方法是禁用不必要的服务。例如,如果您运行的是一个桌面系统(而不是一个服务器),就可以禁用诸如 apache、sendmail 和 mysql 之类的服务,这样可以缩短 init 序列。

由于传统的 init 进程(sysvinit)是一个串行化的进程,因此可对这部分系统进行充分优化。实际上,您可以使用任何方法来对 init 进程进行优化。让我们来了解其中一些方法,以及它们是如何解决这个问题的。前两种方法是基于依赖关系的(即使用依赖关系来提供并行化),第三种方法是一个基于事件的系统(即进程依赖于事件来表示自己何时启动或停止)。



initng

第一种方法 initng (下一代 init)将完全取代异步启动进程的 init,能够更加快速地完成 init 进程。在编写本文时,initng 是一个 beta 版本的产品,其创建者是 Jimmy Wennlund。

initng 背后的基本思想是只要满足了服务的依赖关系就可启动。这样系统就可以在 CPU 和 I/O 之间实现较好的平衡。当从磁盘上加载一个脚本或等待硬件设备启动的同时,可以运行另一个脚本来启动另外一个服务。

initng 工作原理

作为一个基于依赖关系的解决方案,initng 使用自己的初始化脚本集,它们对服务和守护进程的依赖性进行了编码。清单 2 展示了一个示例。这个脚本指定了需要为给定的运行级别启动的服务。该服务具有两个依赖关系,使用 need 关键字定义,分别是 system/initial 和 net/all。在 system/my_service 可以启动之前,这些服务必须是可用的。当这些服务可用时,exec 关键字就开始起作用了。exec 关键字(以及 start 选项)定义了如何使用任何可用的选项启动服务。要停止这个服务,就会使用 exec 关键字以及 stop 选项。

清单 2. 为 initng 定义服务
  1. service system/my_service {
  2.   need = system/initial net/all;
  3.   exec start = /sbin/my_service --start --option;
  4.   exec stop = /sbin/my_service --stop --option;
  5. }
复制代码
您可以使用服务定义对整个系统进行编码,如清单 2 所示。那些没有依赖关系的服务可以立即(并行地)启动,而具有依赖关系的服务则必须等待以安全启动。您可以将 initng 看作一个基于目标的系统。其目标就是要启动的服务。没有进行显式的规划;相反,依赖关系简单地定义了服务初始化的流程,这个过程中隐含着并行化的操作。

使用 initng

initng 包对于典型用法的安装来说相对简单。对于使用非标准包(不是默认配置中出现的包)来说,必须要进行一些汇编工作。

initng 的典型安装需要 initng 发行版(源代码或二进制文件)和 ifiles 发行版。您可以使用 ./configure、make 和 make install 编译自己的 initng 发行版。您必须使用 cmake 来编译 ifiles 文件(这是脚本文件)。根据系统需求的不同,您可能需要创建新的服务/守护进程定义(不过很可能 initng 社区中已经有人这样做了)。然后您还必须修改 LILO 或 GRUB 的配置以指向新的 /sbin/initng。

要控制 initng,需要使用 ngc(比较 telinit 与传统的 init)。它们的语法有些不同,不过功能是相同的。


upstart

替换 init 的另外一种选择是 upstart,它使用的方法与 initng 使用的方法稍有不同。 upstart 是一个基于事件的 init 的替代程序,这意味着服务的启动和停止都基于事件的通信。 upstart 正在由 Scott James Remnant 进行开发,用于 Ubuntu 发行版,不过它想要成为任何 Linux 发行版上 init 的通用替代程序。

upstart 的工作原理

Upstart 要求您更新初始化脚本来支持基于事件的操作模式。upstart 维护自己的在系统启动时启动的 init 进程(对于所有其他方法也是如此)。 首先,init 会发出 startup 事件 —— 这是两个核心事件之一。事件 startup 是由 init 在系统启动时发出的,事件 shutdown 则是在系统关闭时发出的。其他核心事件包括 ctrlaltdel,它说明您按下了 Ctrl-Alt-Delete,或 kbdrequest,它用来说明您按下了 Alt-Up(向上箭头)键组合。

您可以为其他用法创建新事件。例如,您可以创建一个名为 myevent 的任意事件,并使用 echo 命令表示该事件的接收。请使用下面这个简短的作业:

on myevent
exec echo myevent received
console output


这段代码指定在接收到 myevent 事件时将触发该作业。然后代码执行指定的操作(向控制台发出文本)。使用 upstart 配置(/etc/event.d)中给出的文件,可以使用 initctl 工具触发它:

initctl emit myevent


upstart 使用的脚本文件的工作方式类似与传统的 rc init 文件,它们是基于异步事件自发操作的。清单 3 提供了一个简单的样例脚本,它可以接收 3 个事件: startup,启动作业;shutdown 和 runlevel-3,停止作业。shell 执行作业的 script 部分的内容(使用 -e 选项来结束出错脚本)。

清单 3. sysvinit rc 2 脚本的简化 upstart 脚本
  1. start on startup
  2. stop on shutdown
  3. stop on runlevel-3
  4. script
  5. set $(runlevel --set 2 || true)
  6. exec /etc/init.d/rc 2
  7. end script
复制代码

initctl 工具提供了类似于 telinit 的功能,不过增加了一些特定于 upstart 的特性。正如您前面看到的一样,您可以使用 initctl 和 emit 选项为 upstart 生成一个事件。list 选项让您可以通过标识作业状态来深入了解系统操作。它告诉您目前正在等待哪些服务,以及哪些服务目前是活动的。initctl 工具还可以显示用于调试而接收的事件。

Upstart 是 init 的一个有趣的替代程序,并且具有一些独特的优点。实际上已经不存在什么理由再使用运行级别了,因为系统将充分利用硬件进行引导。任何没有给出的硬件都不会触发需要它的任务。Upstart 也可以很好地处理热插拔设备。例如,如果在完成系统引导很长时间以后插入了一块 PCMCIA 网卡,那就会生成 network-interface-added 事件。这个事件会引起动态主机配置协议(Dynamic Host Configuration Protocol,DHCP)作业来对这个网卡进行配置,生成一个 network-interface-up 事件。当为这个新接口分配一个默认路由时,会生成一个 default-route-up 事件。此时,需要网络接口的作业(例如邮件服务器或 Web 服务器)就可以自动启动(如果接口消失,这些服务将会自动停止)。

使用 upstart

编译和安装 upstart 非常简单,并且遵循典型的 configure、make 和 make install 模式。 Upstart 提供了一组示例作业,它们与典型的 init 配置兼容。与 initng 类似,新应用程序必须要根据需求编写自己的作业(可能还需要添加新事件)。不管怎样,部署新的 init 系统都会有一些风险。不过 upstart 的优点当然值得去冒这些风险并执行其他必要的操作。

正如上面介绍的一样,initctl 工具提供了人们对 telinit 所期望的功能。不过 initctl 也为跟踪和调试提供了附加功能。



其他选项

本文中介绍的这两种方法 —— initng 和 upstart —— 并不是只有这两种程序可以替代 init。您还会找到其他的 init 替代程序,例如 runit、pardus、 minit 和 einit。所有这些程序在 Linux 社区中都有一些支持者和一定的促进动力。现在,upstart 可能就是我们要了解的一个,因为它已经被流行的 Ubuntu 发行版采纳为 init 的替代程序。更多信息请参看 参考资料。


使用 bootchart 监视 init 性能

在修改了系统引导进程后,理解修改的内容以及其如何影响系统引导的总时间会非常有帮助。Ziga Mahkovec 开发了一个非常有用的工具 bootchart,可以可视化地表示启动进程的组成。这个工具由几个元素组成,包括一个数据日志记录器工具和一个可视化工具。

数据日志记录器在(bootchartd)是在 init 进程中运行的(通常,在 grub 或 lilo.conf 文件中指定)。在 bootchartd 完成初始化之后,就会将控制权交给真正的 init 进程(通常是 /sbin/init)。Bootchartd 本质上是一个分析器,它每隔一段时间定期对环境进行采样(默认间隔是 200ms)。所谓对环境进行采样,就是说它可以读取当前 CPU 的统计信息、I/O 和空闲事件、磁盘使用情况以及每个活动进程的信息(通过 proc 文件系统)。数据保存在一个临时文件(/var/log/bootchart.tgz)中用于稍后进行二次处理。

Bootchart 然后会使用一个二次处理工具将原始数据转换成一个引导图。这个过程可以在本地使用一个 Java™ 应用程序(bootchart 发行版的一部分)来完成,不过更简单的方法是通过 bootchart 主页上的一个 Web 表单来完成。图 2 展示了引导图表的部分示例。注意这些图将会非常大(这取决于所启动的服务和应用程序)。关于完整示例的链接,请参看 参考资料。

图 2. bootchartd 所创建的引导图片断
引导图





结束语


与 Linux 本身一样,存在很多方法和大量的灵活性对引导时间进行优化。从基于依赖关系的解决方案(如 initng)到基于事件的解决方案(如 upstart),总会有一种优化解决方案能够满足您的要求。使用 bootchart 包,您可以深入理解系统的引导时间是如何分配的,从而进一步进行优化。
发表于 2008-6-21 12:07:51 | 显示全部楼层
文章不错,标题有点歧义。如果是楼主写的,不妨把全文转载过来。
回复 支持 反对

使用道具 举报

发表于 2008-6-21 12:29:07 | 显示全部楼层
文章不错,就是有点难懂。
要不是我对upstart有些了解,还真看不懂。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-6-21 19:06:49 | 显示全部楼层
我也是看的有些困难,第一次接触 upstart
准备配置自动运行时,了解到的。
ubuntu 8.04 server 中的 /etc/init.d/rc.local
最后面加上,启动命令就好。

可是还不知道用不用加上关机时的停止命令,在那里加。这些还不知道。

ps.此文章不是我写的,是转载
回复 支持 反对

使用道具 举报

发表于 2008-6-22 01:01:06 | 显示全部楼层
Post by wide288;1865380

ubuntu 8.04 server 中的 /etc/init.d/rc.local
最后面加上,启动命令就好。

这样做可以解决问题,但这不是upstart正统的方法。/etc/init.d 和/etc/rc*.d 是为了保持和传统启动脚本兼容才保留的,其实upstart已经不需要他们了。upstart正统的启动脚本在 /etc/event.d 下。

你完全可以删掉ubuntu 下 /etc/event.d 的所有文件,自己按照upstart的脚本语法写一套自己的启动/控制脚本。下面列出的文件是我为一个 voip 网关/路由器写的脚本:

  1. /# ls /etc/event.d
  2. console  dhcp6c  dhcp6s  dhcpc  dhcps  image  ipmon  netif  prov  radvd  reboot  resolver  sntp  sys  voice
复制代码

其中
console - 管理 tty console
dhcp6c - 管理 dhcp v6 client
dhcp6s - 管理 dhcp v6 server
dhcpc - 管理 dhcp client
dhcps - 管理 dhcp server
image - 管理 firmware upgrade
ipmon - 管理 ip address/network connection
netif - 网络管理的核心
prov - 管理 voip gateway provisioning
radvd - 管理 radvd 进程
reboot - 管理 reboot
resolver - 管理 DNS
sntp -管理 sntp client
sys - 系统初始化脚本
voice - 管理 voip gateway application

Post by wide288;1865380

可是还不知道用不用加上关机时的停止命令,在那里加。这些还不知道。

这取决于在关机时你是否有特定的事情要做,比如进程必须要正确停止,否则有数据丢失。

方法是在脚本的开始加上

  1. stop on <event>
复制代码

这个 event 通常是 shutdown,这个是系统(upstart)产生的event。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-6-23 20:14:41 | 显示全部楼层

转贴,查到的资料

By Mark Sobell on February 08, 2008 (9:00:00 AM)
原文链接:http://www.linux.com/feature/125977
翻译时间:2008年3月7日
译者:王旭(gnawux at gmail.com)

译注:尽管译者是一位铁杆的Debian粉丝,但也注意 upstart 很久了,就译者本人观点,upstart 应该说是 Ubuntu 所做的众多工作中最为杰出的一个,它将可以极大地加快 Linux 系统启动的过程。尽管它不是惟一的下一代 init 程序,但它已经作为 Ubuntu 的缺省 init 进程工作了相当长的时间,这点将极大有助于程序的成熟;而且,upstart 使用了基于事件的模型,而不是简单的将各个 daemon 并行化,这个架构上的突破也是具有革命性的,下面我们来看这篇文章吧。
因为传统的System V 的 init daemon (Sysvinit)无法很好地处理现代硬件,如热插拔设备、USB硬盘或山村、网络文件系统等,Ubuntu 使用了Upstart init daemon。

本文抽取自最近出版的 A Practical Guide to Ubuntu Linux.

目前已经有多种 sysvinit 的替代产品了,这其中最为著名的一个就是 initng,它已经可以用于 Debian 了,并且在 Ubuntu 上也能工作。在同一位置上,Solaris 使用 SMF (Service Management Facility),而 Mac OS 则使用 launchd。而 Ubuntu 则更倾向于集这些软件的优点于一身。
Sysvinit daemon 是一个基于运行级别的初始化程序,它使用了运行级别(如单用户、多用户等)并通过从 /etc/rc?.d 目录到 /etc/init.d 目录的初始化脚本的链接来启动与终止系统服务。自从 Feisty 开始,Ubuntu 转向了 Upstart init daemon,并开始将Sysvinit 设置转换为 Upstart 的设置。本文探讨 Upstart 和残存的部分 Sysvinit 遗迹:/etc/rc?.d 和 /etc/init.d 目录以及运行级别的概念。
Upstart init daemon 是基于事件的,当系统中的什么情况发生变化时,它会运行某个特定的程序。这里被运行的程序多半是用来启动或终止服务的脚本。这个配置方式和systemv 在系统进入某个运行级别的时候运行init脚本的链接的概念实际上是非常类似的,只不过 upstart 更加灵活一些。Upstart 不仅能在运行级别改变的时候启动或终止服务,也能在接收到系统发生其他改变的信息的时候启动或终止服务。这些系统的改变被称为“事件”。例如,当 upstart 从 udev 接收到运行时文件系统加载、打印机安装或其他类似的设备添加或删除的信息,并采取相应的行动。Upstart 也可以在系统启动、关闭或某个任务状态改变的时候启动或关闭服务。
Upstart由五个包组成,(Ubuntu 中)它们都会被缺省安装:

  1.     *
  2.       upstart 提供Upstart init daemon 和 initctl 工具。
  3.     *
  4.       upstart-logd 提供 logd daemon 和 logd 服务的工作定义文件。
  5.     *
  6.       upstart-compat-sysv 提供了 rc 任务的工作定义文件,并提供了 reboot, runlevel, shutdown, telinit 等工具,以便与 sysvinit 相兼容。
  7.     *
  8.       startup-tasks 提供了系统启动任务的工作定义文件。
  9.     *
  10.       system-services 提供了 tty 服务的工作定义文件。
复制代码

定义

有几个名词帮助我们理解 init 相关的东西。事件(event)是 init 可以得到的状态变更信息。几乎系统所有的内部或外部状态变更都可以触发一个事件。比如,引导程序会触发启动(startup)事件,系统进入运行级别2会触发运行级别2(runlevel 2)事件,而文件系统加载则会触发路径加载(path-mounted)事件,拔掉或安装一个热插拔或USB设备(如打印机)也会触发一个时间。用户还可以通过 initctl emit 命令来手动触发一个事件。
一个工作(job)是 init 可以理解的一系列指令。典型的指令包括一个程序(二进制文件或是脚本)和事件的名称。Upstart init daemon 会在事件触发的时候运行相应的程序。用户可以分别用 initctl start 和 stop 命令手动启动或终止一项工作。工作又可以分为任务和服务。
任务是运行、并在执行结束后返回到等待状态的工作。
服务是那些通常不会自己结束的工作。比如,logd daemon 和 gettys 就被实现为服务。init daemon 会监测每个服务的状态,如果服务出现问题会重启服务,在某些事件触发时或手工停止时会杀死服务。
/etc/event.d 目录下包含着一系列的工作定义文件(定义了 upstart init daemon 运行的工作的文件)。最初,这个目录由 Upstart 包来生成。在 Feisty 之后的 Ubuntu 中,被安装的服务会向这个目录中添加控制服务的文件,替代哪些安装到 /etc/rc?.d 和 /etc/init.d 目录的文件。
Upstart init daemon 的核心是一个状态机。它持续跟踪各个工作的状态,当有事件触发的时候,跟踪工作的状态改变。当 init 跟踪到一个工作的状态从一个转变到了另一个的时候,就可能会执行工作的命令或是终止工作。
system V 的 init daemon 通过改变运行级别来启动或停止服务。而使用 Upstart init daemon 的 Ubuntu 系统没有运行级别的概念。为了将基于运行级别的系统平滑移植到基于事件的系统,并为面向其他发布版的软件提供一定的兼容性,Ubuntu 使用 Upstart 模拟了运行级别。
在 /etc/event.d/rc? 文件中定义的 rc? 工作会运行 /etc/init.d/rc 脚本,这个脚本会运行链接到 /etc/rc?.d 目录中的 /etc/init.d 中的启动脚本,以模拟 SysVinit 的行为。当系统进入一个运行级别的时候,rc? 工作就会运行这些脚本。同时,Upstart 提供了 runlevel 和 telinit 工具以提供与 SysVinit 的兼容性。
使用 initctl (init control) 工具,具有 root 权限的管理员可以和 Upstart init daemon 通信。这个工具可以用来启动、停止或报告(report)一项工作。 比如,initctl list 命令会列出所有的工作和它们的状态:

  1. $ sudo initctl list
  2. logd (stop) waiting
  3. rc-default (stop) waiting
  4. rc0 (stop) waiting

  5. tty5 (start) running, process 4720
  6. tty6 (start) running, process 4727
复制代码

要获得更详细的信息,可以参考 initctl 的 man page 或本节的例子。使用 initctl help 命令 (help 前没有横杠)可以列出 initctl 的命令列表。此外,也可以用 initctl list –help 来列出 list 命令的帮助信息,当然,将 list 换乘其它的 initctl 命令会得到该命令对应的信息。start, stop 和 status 工具是 initctl 的链接,会直接运行 initctl 的对应命令。
工作(Job)

/etc/event.d 目录下的每个文件都定义了一个工作,其中至少应该包含一个事件和一个命令。当事件被触发的时候,init 执行对应的命令。本节将介绍管理员自定义的工作和 Upstart 包中包含的工作。

下面的管理员自定义的工作使用 exec 关键字执行了一条 shell 命令。实际上,也可以用这个关键字执行一个 shell 脚本或一个二进制可执行文件。

  1. $ cat /etc/event.d/mudat
  2. start on runlevel 2
  3. exec echo “Entering multiuser mode on ” $(date) > /tmp/mudat.out
复制代码

这个文件定义了一个任务:当系统进入到多用户模式(运行级2)的时候执行 echo 命令。这个命令会向 /tmp/mudat.out 文件写出一条包含日期时间消息。shell 会运行 date 命令替换其中的内容。在任务结束后, mudat 任务会停止并进入等待状态。
在下一个的例子中,cat 命令展示了 /tmp/mudat.out 文件的内容和 initctl list 命令关于这个任务的输出(status 工具也可以得到同样的信息):

  1. $ cat /tmp/mudat.out
  2. Entering multiuser mode on Tue Jul 10 17:34:39 PDT 2007

  3. $ sudo initctl list mudat
  4. mudat (stop) waiting
复制代码

如果 exec 命令行中包含 shell 的特殊字符, init 会运行 /bin/sh(dash 的符号链接)并把命令行交给它来处理。否则,exec 会直接运行命令行。如果要执行多个 shell 命令,可以把他们放到脚本文件中并运行脚本,或是使用 script….end script (下面会介绍)。
Upstart initdaemon  只能监测哪些使用 exec 运行的工作(服务),无法监测使用 script…end script 运行的工作。换句话说,服务应该使用 exec 运行,而任务则可以使用任意的方法。
myjob 示例

用户也可以自己定义一个事件,并让一个工作被这个事件触发。如下的 myjob 工作定义文件定义了一个被 hithere 事件触发的工作:

  1. $ cat /etc/event.d/myjob
  2. start on hithere
  3. script
  4.         echo “Hi there, here I am!” > /tmp/myjob.out
  5.         date >> /tmp/myjob.out
  6.         end script
复制代码

myjob 文件提供了另一种运行命令的方法:在 script 和 end script 关键字之间包含了两行命令。这两个关键字常常导致 init 去运行 /bin/sh。例中的命令将一条消息和日期输出到了 /tmp/myjob.out 文件。现在可以使用 initctl emit 命令触发这个工作。如下,init 展示了 myjobs 在我们的触发下所经历的各个状态:

  1. $ sudo initctl emit hithere
  2. hithere
  3. myjob (start) waiting
  4. myjob (start) starting
  5. myjob (start) pre-start
  6. myjob (start) spawned, process 6064
  7. myjob (start) post-start, (main) process 6064
  8. myjob (start) running, process 6064
  9. myjob (stop) running
  10. myjob (stop) stopping
  11. myjob (stop) killed
  12. myjob (stop) post-stop
  13. myjob (stop) waiting

  14. $ cat /tmp/myjob.out
  15. Hi there, here I am!
  16. Sat Jul 7 20:19:13 PDT 2007

  17. $ sudo initctl list myjob
  18. myjob (stop) waiting
复制代码

在上面的例子里,cat 展示了 myjob 产生的输出,initctl 展示了工作的状态。同样也可以用 initctl start myjob(或直接用 start myjob)来运行它。initctl start 十个非常有用的命令,这样你就可以在没有事件的情况下启动一个工作。比如,你可以用 initctl start mudat 来直接运行前面例子中的 mudat 工作而不会触发 runlevel 2 事件。
指定带参数的事件

telinit 和 shutdown 工具发送带有参数的 runlevel 事件。比如,shutdown 发送 runlevel 0,telinit 2 会发送 runlevel 2 事件。你可以在工作定义中用如下格式匹配这些事件:
start | stop on event [arg]

其中 event 是一个事件,而 arg 是一个可选参数。要在系统进入 runlevel 2 的时候停止一个工作,可以指定 stop on runlevle 2,也可以指定 runlevel [235] 来匹配运行级 2, 3 和 5,或用 runlevel [!2] 来匹配 2 之外的运行级。
尽管 Upstart 会忽略掉多余的事件参数,但工作定义文件中的事件名称里的参数必须在事件中存在。比如,没有参数的 runlevel 可以匹配所有的 runlevel 事件,不论是否有参数,但 runlevel S arg2 将不会匹配任何事件,因为 runlevel 事件只会带有一个参数。
/etc/event.d 中的工作定义文件

随着 Ubuntu 从 SysVinit 向 Upstart 的迁移,更多地工作会在 /etc/event.d 文件中定义。本节介绍一些 Upstart 包放在这个目录中的工作定义文件。

/etc/event.d/rc2 工作定义文件定义了 rc2 任务,这和其他的 rc? 任务没什么区别。rc2 任务在系统进入到多用户模式的时候会被触发(事件名称是 runlevel 2);当系统进入到其它任意运行级的时候(runlevel [!2])会结束。脚本的第一个部分调用 runlevel 工具,它会让系统显示自己在运行级2 (当然,实际上已经没有运行级这个玩意儿了)并给两个变量赋值。接下来的工作由 exec 命令完成,它会使用参数 2 运行 /etc/init.d/rc 脚本。这个脚本使用相应的参数调用 /etc/rc?.d 目录中的那些链接。这里 rc2 任务会运行 /etc/rc2.d 下的符号链接对应的 init 脚本。

  1. $ cat /etc/event.d/rc2
  2. # rc2 - runlevel 2 compatibility
  3. #
  4. # This task runs the old sysv-rc runlevel 2 (”multi-user”) scripts. It
  5. # is usually started by the telinit compatibility wrapper.

  6. start on runlevel 2

  7. stop on runlevel [!2]

  8. console output
  9. script
  10.         set $(runlevel –set 2 || true)
  11.         if [ “$1″ != “unknown” ]; then
  12.                 PREVLEVEL=$1
  13.                 RUNLEVEL=$2
  14.                 export PREVLEVEL RUNLEVEL
  15.         fi

  16.         exec /etc/init.d/rc 2
  17. end script
复制代码

tty 服务

如下是一个在 tty1 上启动并监视 getty 进程的服务的工作定义文件:

  1. $ cat /etc/event.d/tty1
  2. # tty1 – getty
  3. #
  4. # This service maintains a getty on tty1 from the point when
  5. # the system is started until it is shut down again.

  6. start on runlevel 2
  7. start on runlevel 3
  8. start on runlevel 4
  9. start on runlevel 5

  10. stop on runlevel 0
  11. stop on runlevel 1
  12. stop on runlevel 6

  13. respawn
  14. exec /sbin/getty 38400 tty1
复制代码

这个服务由 runlevel 2 到 5 (多用户模式)来触发,启动 getty 进程,并在系统关闭、重启或进入单用户模式,即运行级 0,1 和 6 时触发来关闭该服务。respawn关键字告诉 init 在服务终止后重启服务,而 exec 命令是让 getty 进程以 38400 波特率运行在 tty1。如下,initctl 工具显示该服务处于启动状态,进程ID 4747,ps 命令显示该服务的进程:

  1. $ sudo initctl list tty1
  2. tty1 (start) running, process 4747

  3. $ ps -ef | grep 4747
  4. root   4747   1 0 Jul02 tty1   00:00:00 /sbin/getty 38400 tty1
复制代码

rc-default 任务和 inittab

在 SysVinit 中,/etc/inittab 文件通过 initdefault 项告诉 init 在系统启动的时候进入哪个运行级,而 Ubuntu 没有 inittab 文件,缺省的,Upstart init daemon (使用 rc-default 任务)引导系统进入多用户模式(缺省运行级为2)。如果希望系统启动进入其他运行级别,那么就创建一个 inittab 文件。如下会让系统缺省进入单用户模式 (runlevel S):

$ cat /etc/inittab
:id:S:initdefault:

当系统进入到单用户(修复)模式,如果系统的root帐号没有被锁定,init 会在显示 root 提示符之前要求输入 root 密码。否则,它会不要求输入密码而直接显示 root 提示符。

注意:不要将系统设置启动到运行级别 0 或 6,这样系统将永远无法正常启动。要直接进入多用户模式(运行级 2),如果有 inittab 删除这个文件,或者用上述的例子,将里面的 S 替换成 2.

Upstart的未来

从 SysVinit 到 Upstart 的迁移涉及到了 Linux 系统的很多部分。要让这个转换尽量平滑并且引入尽量少的问题,Upstart 团队决定通过多个 release 来完成这个迁移。

Ubuntu 从 Feisty 开始使用 Upstart init daemon。在 Feisty 和 Gutsy+2 之间,Ubuntu 将完成 SysVinit 到更加干净、更加灵活的 Upstart 的迁移。随着越来越多的服务被放到 Upstart 的控制之下,/etc/event.d 目录下的内容将会替代 /etc/init.d 和 /etc/rc?.d 目录下的内容。运行级将不再作为 Ubuntu 正式支持的特征,虽然它们可能为了保持与第三方软件的兼容性而继续保留。最终 Upstart 将会替换掉 crond。



原文地址 http://labs.chinamobile.com/blog ... tu-%E7%9A%84%E5%9F%
回复 支持 反对

使用道具 举报

发表于 2008-6-23 21:10:24 | 显示全部楼层
回复 支持 反对

使用道具 举报

发表于 2008-6-23 23:55:55 | 显示全部楼层
此帖甚好,加精了。感谢biinn和wide288的文章和freeflying的链接。
大家有任何upstart的问题时就可以参考本帖了。
另外,能否麻烦楼主有空的时候把第一楼的链接改成全文,再附上原文链接呢?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表