快捷搜索:   nginx

Ubuntu : 基于upstart的启动过程

ubuntu从6.10开始逐步用upstart代替原来的sysinit,进行服务进程的管理。也正是从6.10开 始,ubuntu的过程开始变得 有点“变幻莫测”。也没办法,这是正在开发中的upstart不可避免的。为了对原有的init实现向后兼容,upstart可以说是在表象上保留了大部 分原来init的特性,因而目前linux初始化进程名仍然叫init,而改变的核心,乃是Event机制。理解并讲清楚这个改变的重要意义和内在机理可 不是件容易的事,所以我只打算研究一下目前upstart(0.3.9, ubuntu 8.04)在系统中的表象行为。[注:据Scott James Remnant在其博客上所说,upstart 0.5.0版本将在未来几周发布。]

    关于系统启动,熟悉Linux的人大多应该知道,init进程(PID=1)乃是所有进程的父进程,所有进程由它控制。init进程的时间是内核完成 文件系统的加载后。那么init进程是如何开启系统中的其它进程的呢?在阐述这个问题之前,大致地说明一下目前ubuntu中与init相关的几个目录和 应用程序,可以方便后面的论述。这些目录和程序包括:

    前三个是应用程序(注意哦,它们都不是shell脚本),可以理解为是由内核调用的。关于它们的功能,从manpage查看就可以了。我们的重点是后面给出的三个目录。

    首先是/etc/event.d/目录,这是upstart的核心,upstart不同于原有的init的地方就在于它引入了event机制。Event 机制通俗的讲就是将所有进程的触发、停止等等都看作event(事件)。/etc/event.d/中就存放了目前upstart需要识别的event。 这其中主要有三种rc-default, rcX(x=0,1,...6,S)以及ttyX。这rc-default就类似于那大名鼎鼎的inittab文件,它就是设置默认运行级别的 [注:upstart中实际并没有运行级别的概念,这么称呼是为了init向后的兼容性]。现在你应该知道了ubuntu里没有了inittab文件后该 到哪里设置默认运行级别的了吧!cat rc-default一下吧!rcX文件是发生相应运行级别事件(可以注意到event这个词在upstart里真是无处不见啊)时,需要运行程序的脚 本,而ttyX则是设置伪终端数目的,也就是你Ctrl+Alt+F(1~6)调出的那个Console。我们以rc2为例,cat rc2:

   

..

    不去考虑细节,只要注意到前两行和倒数第二行就可以了。可以看到,rc2文件是定义在发生运行级别2的时候所要执行的东西,核心就是这句:exec /etc/init.d/rc 2。这样,我们就可以自然地过渡到下一个重要的目录,/etc/init.d/了。

    你可以ls /etc/init.d/看一下里面的内容,对它有个大致的了解。/etc/init.d/中存放的是服务(services)或者任务(tasks)的 执行脚本。可以这么说,只要你安装了一个程序(特别是服务程序daemon),它可以在系统启动的时候运行,那么它必定会在/etc/init.d/中有 一个脚本文件。我们还回到上面的rc2文件,它执行了一个exec /etc/init.d/rc 2的命令。也就是说,给/etc/init.d/rc脚本传递了一个参数"2",让它执行。我们仔细查看一下rc脚本(很长,耐心点),能看到这样的一 段:

   

    这说明,当给rc脚本传递一个数字参数"X"的时候,它在经过一系列的设置后,将会开始执行/etc/rcX.d/下S开头的脚本。这就过渡到下一个目录/etc/rcX.d/了。

    进入/etc/rcX.d/,ls -l /etc/rcX.d/看看有些什么内容?哈哈,没错,都是一些到/etc/init.d/中脚本的符号链接。不同的是它们的开头加上了S和一个数字。熟 悉原本init的人应该知道,S表示在启动时运行,数字则表示执行的先后顺序。

    这样一来,upstart管理的ubuntu启动过程应该就清楚了。梳理一下:
    1,内核启动init
    2,init找到/etc/event.d/rc-default文件,确定默认的运行级别(X)
    3,触发相应的runlevel事件,开始运行/etc/event.d/rcX
    4,rcX运行/etc/init.d/rc,传入参数X
    5,/etc/init.d/rc脚本进行一系列设置,最后运行相应的/etc/rcX.d/中的脚本
    6,/etc/rcX.d/中的脚本按事先设定的优先级依次启动,直至最后给出登录画面(启动X服务器和GDM)

    理解了这些,手动配置开机服务的启动与否就很简单了。Ubutnu默认的启动级别是2,不想启动的程序,只要把相应的符号链接从/etc/rc2.d/中删去即可!

[转载2]

—————————————————————————————————————————————

本文抽取自最近出版的 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 中)它们都会被缺省安装:

upstart 提供Upstart init daemon 和 initctl 工具。
upstart-logd 提供 logd daemon 和 logd 服务的工作定义文件。
upstart-compat-sysv 提供了 rc 任务的工作定义文件,并提供了 reboot, runlevel, shutdown, telinit 等工具,以便与 sysvinit 相兼容。
startup-tasks 提供了系统启动任务的工作定义文件。
system-services 提供了 tty 服务的工作定义文件。

定义
有几个名词帮助我们理解 init 相关的东西。事件(event)是 init 可以得到的状态变更信息。几乎系统所有的内部或外部状态变更都可以触发一个事件。比如,引导程序会触发启动(startup)事件,系统进入运行级别2会 触发运行级别2(runlevel 2)事件,而文件系统加载则会触发路径加载(path-mounted)事件,拔掉或安装一个热插拔或USB设备(如打印机)也会触发一个时间。用户还可 以通过 initctl emit 命令来手动触发一个事件。

一个工作(job)是 init 可以理解的一系列指令。典型的指令包括一个程序(二进制文件或是脚本)和事件的名称。Upstart init daemon 会在事件触发的时候运行相应的程序。用户可以分别用 initctl start 和 stop 命令手动启动或终止一项工作。工作又可以分为任务和服务。

任务(task)是运行、并在执行结束后返回到等待状态的工作。

服务(service)是那些通常不会自己结束的工作。比如,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 命令会列出所有的工作和它们的状态:

$ sudo initctl list
logd (stop) waiting
rc-default (stop) waiting
rc0 (stop) waiting

tty5 (start) running, process 4720
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 脚本或一个二进制可执行文件。

$ cat /etc/event.d/mudat
start on runlevel 2
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 工具也可以得到同样的信息):

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

$ sudo initctl list mudat
mudat (stop) waiting

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

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

$ cat /etc/event.d/myjob
start on hithere
script
echo “Hi there, here I am!” > /tmp/myjob.out
date >> /tmp/myjob.out
end script

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

$ sudo initctl emit hithere
hithere
myjob (start) waiting
myjob (start) starting
myjob (start) pre-start
myjob (start) spawned, process 6064
myjob (start) post-start, (main) process 6064
myjob (start) running, process 6064
myjob (stop) running
myjob (stop) stopping
myjob (stop) killed
myjob (stop) post-stop
myjob (stop) waiting

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

$ sudo initctl list myjob
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 runlevel 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 脚本。

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

start on runlevel 2

stop on runlevel [!2]

console output
script
set $(runlevel –set 2 || true)
if [ “$1″ != “unknown” ]; then
PREVLEVEL=$1
RUNLEVEL=$2
export PREVLEVEL RUNLEVEL
fi

exec /etc/init.d/rc 2

end script

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

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

start on runlevel 2
start on runlevel 3
start on runlevel 4
start on runlevel 5

stop on runlevel 0
stop on runlevel 1
stop on runlevel 6

respawn
exec /sbin/getty 38400 tty1

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

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

$ ps -ef | grep 4747
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.

顶(0)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论