upstart 分析
前言
众所周知,/sbin/init 是linux内核启动后运行的首个用户进程,早期的init程序由sysvinit包提供。 SysVinit软件包包含了一组控制系统最基本函数的进程,它包含了系统初始化程序init,init 是系统启动时被kernel最先启动的进程,它控制着其它所有进程的启动、运行以及停止。Sysvinit 中的init daemon 是一个基于运行级别的初始化程序,它使用了运行级别(如单用户、多用户等)并通过从 /etc/rc?.d 目录到 /etc/init.d 目录的初始化脚本的链接来启动与终止系统服务。例如大家熟悉的级别3进入text模式,级别5为graphis模式;以及/etc/rc?.d 目录下数字代表启动的优先级,K代表停止某个服务,S代表启动服务等。
Ubuntu 从6.10版本开始使用 upstart来代替sysvinit, upstart包提供了新的init daemon。Upstart init daemon 本质上不是基于运行级别的启动init进程,但为了跟传统的sysvinit兼容,upstart提供了兼容的runlevel和telinit等工具,以及也使用/etc/rc?.d 作为启动或者停止某个服务。但运行级别3不在是text模式,而且也不能通过改变运行级别来改变进入text模式。
Upstart 初探
Upstart 跟Sysvinit 本质上一样,都是用于linux开机自动启动某些后台服务,同时还承担监控这些服务运行状态的功能。但Sysvinit中的init daemon 不能解决诸如系统接收到打印机等热插拔事件安装时来启动某种特定的服务这类问题,而upstart则可以。
ustart init daemon 是基于事件的,当系统中的什么情况发生变化时,它会运行某个特定的程序。这里被运行的程序多半是用来启动或终止服务的脚本。这个配置方式和systemv 在系统进入某个运行级别的时候运行init脚本的链接的概念实际上是非常类似的,只不过 upstart 更加灵活一些。Upstart 不仅能在运行级别改变的时候启动或终止服务,也能在接收到系统发生其他改变的信息的时候启动或终止服务。这些系统的改变被称为“事件”。例如,当 upstart 从 udev 接收到运行时文件系统加载、打印机安装或其他类似的设备添加或删除的信息,并采取相应的行动。Upstart 也可以在系统启动、关闭或某个任务状态改变的时候启动或关闭服务。
在upstart中存在如下几个概念:
Process: process 是由jobs定义的services或者task,它将被init daemon 运行。 每个job可以定义一个或者多个不同的process,分别在其生命周期的不同状态运行。Process 定义如下:
Exec COMMAND
Scritp … end script
Pre-start exec|script
Post-start exec|script
Pre-stop exec|script
Post-stop exec|script
Event: 事件,事件(event)是 init 可以得到的状态变更信息。几乎系统所有的内部或外部状态变更都可以触发一个事件。比如,引导程序会触发启动(startup)事件,系统进入运行级别2会 触发运行级别2(runlevel 2)事件,而文件系统加载则会触发路径加载(path-mounted)事件,拔掉或安装一个热插拔或USB设备(如打印机)也会触发一个时间。用户还可 以通过 initctl emit 命令来手动触发一个事件。事件定义格式如下:
start on Event [[KEY=] Value … and| or …]
stop on Event [[KEY=] Value … and| or …]
Job: 一个工作(job)是 init 可以理解的一系列指令。典型的指令包括一个程序(二进制文件或是脚本)和事件的名称。Upstart init daemon 会在事件触发的时候运行相应的程序。用户可以分别用 initctl start 和 stop 命令手动启动或终止一项工作。工作又可以分为任务和服务。
My job 示例
用户也可以自己定义一个事件,并让一个工作被这个事件触发。如下的 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
$ cat /tmp/myjob.out
Hi there, here I am!
Sun Apr 24 13:25:43 CST 2011
$ sudo initctl list | grep myjob
myjob (stop) waiting
在上面的例子里,cat 展示了 myjob 产生的输出,initctl 展示了工作的状态。同样也可以用 initctl start myjob(或直接用 start myjob)来运行它。initctl start 十个非常有用的命令,这样你就可以在没有事件的情况下启动一个工作。比如,你可以用 initctl start mudat 来直接运行前面例子中的 mudat 工作而不会触发 runlevel 2 事件。
Upstart 事件监控实现
在init daemon 中需要监测某个进程的状态,例如存在repasw机制,监测getty进程是否退出,如果退出则需要再次启动getty以便用户登录。 为实现对事件的监测,要么采用轮询要么采用事件驱动的回调机制,upstart采用事件驱动机制。为实现基于事件驱动的机制,通常涉及跨进程调用,Upstart利用dbus来完成iPC通信。但upstart init daemon启动时dbus-daemon并没有运行,实际dbus-daemon是由upstart来启动。因此,upstart 采用 private D-bus 连接(unxi:address=/com/ubuntu/upstart)来实现IPC;其它进程(如telinit等)通过该连接来通知init daemon 产生某个事件。
Upstart init daemon 运行时会产生startup事件,在/etc/init下很多job都start on 该事件,其中比较著名的是:
rc-default job . 该job会调用telinit N来登录N用户级别,同时产生runevel事件。而runevel事件触发ttyN job,从而调用getty程序。对于图形界面的登录程序如gdm的触发为另外的条件,后面再续。
为完成监控内核事件,例如usb的热插拔,upstart提供upstart-udev-bridge.conf job来完成该功能,即在/etc/init 下存在upstart-udev-bridge.conf 文件。当udev event发生时,该job将产生 upstart事件通知init。这类事件通常采用如下格式:
“<udev_subsystem>-device-<udev_action>
因此upstart-udev-bridge.conf 可能为:
net-device-added net-device-removed graphics-device-added drm-device-added
用户自动登录和定制XWindows
在工作中,常常存在如下需求:
- 开机运行linux,自动登录,无需输入用户名密码;
- 登录后自动运行我们的GUI软件,且无需ubuntu等桌面环境
在使用upstart的ubuntu系统中,控制登录的脚本主要是:
- /etc/init/ttyN.conf
- /etc/init/gdm.conf
Gdm.conf用户启动gdm,即ubuntu的图形桌面;由于我们并不需要桌面,因此首先删除gdm.conf. 删除gdm.conf将不能启动linux GUI桌面,但由于我们开机运行的GUI软件本质还是一个X11 Client程序,因此需要启动X Server。这时可以startx 脚本就派生用场了,在传统的用户text模式下,大都通过startx进入GUI系统。因此我们可以配置.xinitrc 脚本: 例如如下:
// user gui programmer
Metacity
为保证用户登录后即运行startx,可以在~/.bash_profile 中写入:
exec startx
上述过程保证了用户登录后即运行startx 启动了用户程序,但如何实现用户自动登录呢? 这需要修改/etc/init/ttyN.conf ,默认使用getty程序登录,该程序不支持用户自动登录。Linux下有一个叫minigetty 程序支持该功能,下载并修改/etc/init/ttyN.conf 文件,大致如下:
Exec /sbin/mingetty –autologin root tty1
其它init 实现
Systemd是一个比upstart设计思路更超前的init系统,见http://0pointer.de/blog/projects/systemd.html。 其核心是为了加快linux的启动速度,研究如何并行化启动init以后的用户进程,可以参考 http://linuxtoy.org/archives/more-than-upstart-systemd.html
参考文献
http://linuxtoy.org/archives/more-than-upstart-systemd.html
http://labs.chinamobile.com/mblog/225_2804
----------------------------------------------------------------
RHEL6启动过程
在 Red Hat Enterprise Linux 6 中,sysvinit 软件包中的 init 已经由 Upstart 替换,它是基于事件的 init 系统。这个系统可处理在引导过程中的任务和服务启动,在关机过程中关闭它们并在系统运行时监控它们。有关 Upstart 的详情请参考 init(8) man page。Upstart 识别为任务的进程是由 /etc/init 目录中的文件定义。可以说rhel6的启动流程发生了比较大的变化。对于熟悉其流程非常重要,对系统的排错有莫大的帮助。
----------------------------------------------------------------------------------------------------------------
1、BIOS加电,进行硬件自检。然后产生int 19的终端,读取第一个可引导的设备,比如自检,初始化各种芯片,控制器,与端口;包括显示器,内存,键盘,软驱,串口等等最终,POST(Power-On Self Test)执行INT 19h中断,找到可以启动的磁盘,并将boot程序装入内存,并将控制权交给OS的boot程序。当BIOS INT 19h被执行以后,系统进入OS Booting阶段。
----------------------------------------------------------------------------------------------------------------
2、一块硬盘,有一个主引导记录,就是0面0道1扇区,又称MBR。MBR是由BIOS自动装载到内存中并CPU跳转过去执行的。主引导记录(Master Boot Record,缩写:MBR),又叫做主引导扇区,是计算机开机后访问硬盘时所必须要读取的首个扇区,它在硬盘上的三维地址为(柱面,磁头,扇区)=(0,0,1)。共512 Byte。
______________________________________________________________________________
| | | |
| 引导代码 446 Byte | 分区表 64 Byte | 结束符2 Byte |
|________________________________________|_____________________|______________|
------------------------------------------------------------------------—---------------------------------------
3、读取第一个可引导的设备的MBR的BootLoader程序,在rhel系统上是Grub.
stage1:
/boot/grub中的stage1文件大小为512b,它是引导扇区中引导程序(前446字节为引导程序)的备份文件,功能是用来装载 stage1.5或stage2的。
stage1.5:
因为STAGE2较大,通常都是放在一个文件系统当中的,但是STAGE1并不能识别文件系统格式,所以才需要stage1.5来引导位于某个文件系统当中的STAGE2,根据文件系统格式的不同,STAGE1.5也需要相应的文件,如:e2fs_stage1_5,fat_stage1_5,分别用于识别ext和fat的文件系统格式。但是,当stage1加载1.5时,原则上是不认识ext文件系统的,应该也是无法找到1.5程序的,而实际上在安装grub时,程序已经把1.5程序写到硬盘最前面的32K中,因为紧连着MBR的一段硬盘空间通常是空白无用的,grub就把stage1.5程序写到这个地方,这个地方没有文件系统,stage1程序可以直接去加载stage1.5。
----------------------------------------------------------------------------------------------------------------
4、Grub进程第二阶段引导,读取/boot/grub/grub.conf配置文件,根据配置文件中的定义,加载相应的内核,内核再加载相应的硬件驱动,进行必要的基础硬件初始化
stage2:
grub能让用户以选项方式将OS加载、改变参数、修改选项,这些全都是stage2程序的功能。stage2可以去获取grub.conf以及menu.lst等文件的内容。
[root@osdba~]# ls /boot/
config-2.6.32-71.el6.i686 <===系统kernel的配置文件,内核编译完成后保存的就是这个配置文件
efi <===Extensible Firmware Interface(EFI,可扩展固件接口)是 Intel 为全新类型的 PC 固件的体系结构、接口和服务提出的建议标准。
grub <===开机管理程序grub相关数据目录
initramfs-2.6.32-71.el6.i686.img <===虚拟文件系统文件(RHEL6用initramfs代替了initrd,他们的目的是一样的,只是本身处理的方式有点不同)
initrd-2.6.32-71.el6.i686.img <===此文件是linux系统启动时的模块供应主要来源,initrd的目的就是在kernel加载系统识别cpu和内存等内核信息之后,让系统进一步知道还有那些硬件是启动所必须使用的;
symvers-2.6.32-71.el6.i686.gz <===模块符号信息
System.map-2.6.32-71.el6.i686 <===是系统kernel中的变量对应表;(也可以理解为是索引文件)
vmlinuz-2.6.32-71.el6.i686 <===系统使用kernel,用于启动的压缩内核镜像。
[root@osdba~]# cat /boot/grub/grub.conf
# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE: You have a /boot partition. This means that
# all kernel and initrd paths are relative to /boot/, eg.
# root (hd0,0)
# kernel /vmlinuz-version ro root=/dev/sda3
# initrd /initrd-[generic-]version.img
#boot=/dev/sda
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title Red Hat Enterprise Linux (2.6.32-71.el6.i686)
root (hd0,0)
kernel /vmlinuz-2.6.32-71.el6.i686 ro root=UUID=bfd97ccd-15df-4e0f-8a66-b2da62213f62
rd_NO_LUKS rd_NO_LVM rd_NO_MD rd_NO_DM
LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=us crashkernel=auto rhgb quiet
initrd /initramfs-2.6.32-71.el6.i686.img
default #启动系统时在人为不干预的情况下,默认读取哪一个title,如果安装了多个不同版本内核或者安装了不用的操作系统,默认会产生多个title,#0第一个,1第二个,以此类推。
timeout #开机等待用户的超时时间,单位为秒。在超时时间结束时,如果用户没有人为选择,则以default指定的title读取。
splashimage=(hd0,0)/grub/splash.xpm.gz #grub的背景图案
hiddenmenu #隐藏菜单
title Red Hat Enterprise Linux (2.6.32-71.el6.i686) #title后为指定的菜名字
root (hd0,0)
#root 这里不是管理员的意思,这里的root表示为根文件系统,hd代表硬盘(不区分IDE、SCSI),第一个0:第一个硬盘,第二个0:第一个分区。
#这里的意思是定义第一个硬盘的第一个分区为根文件系统。
kernel /vmlinuz-2.6.32-71.el6.i686 ro root=UUID=bfd97ccd-15df-4e0f-8a66-b2da62213f62 rd_NO_LUKS rd_NO_LVM rd_NO_MD rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=us crashkernel=auto rhgb quiet
initrd /initramfs-2.6.32-71.el6.i686.img
#kernel /vmlinuz-2.6.32-71.el6.i686
由boot loader的管理而开始读取内核文件后,接下来,Linux 就会将内核解压缩到主存储器当中, 并且利用内核的功能,开始测试与驱动各个周边设备,包括储存设备、CPU、网卡、声卡等等。 此时 Linux 内核会以自己的功能重新检测一次硬件,而不一定会使用 BIOS 检测到的硬件信息。也就是说,内核此时才开始接管 BIOS 后的工作。内核一般来说,它会被放置到 /boot 里面,并且取名为 /boot/vmlinuz。
#ro root=UUID=bfd97ccd-15df-4e0f-8a66-b2da62213f62
Linux内核是可以通过动态加载内核模块的 (就请想成驱动程序即可),这些内核模块就放在 /lib/modules/目录内。由于模块放到磁盘根目录内(这就是为什么/lib不可以与/ 分别放在不同的分区原因), 因此在开机的过程中内核必须要挂载根目录,这样才能够读取内核模块提供加载驱动程序的功能。 而且为了担心影响到磁盘内的文件系统,开机过程中根目录是以只读的方式来挂载的。
#rd_NO_LUKS rd_NO_LVM rd_NO_MD rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc
KEYTABLE=us crashkernel=auto rhgb quiet
Speeding up the Boot Process
If you want to speed up the boot process, you can specify as much information for dracut on the kernel command as possible. For example, you
can tell dracut, that you root partition is not on a LVM volume or not on a raid partition, or that it lives inside a specific crypto LUKS encrypted
volume. By default, dracut searches everywhere. A typical dracut kernel command line for
a plain primary or logical partition would contain:
rd_NO_LUKS rd_NO_LVM rd_NO_MD rd_NO_DM (elinks /usr/share/doc/dracut-004/dracut-rhel6.html or man 8 dracut)
This turns off every automatic assembly of LVM, MD raids, DM raids and crypto LUKS.
Keyboard Settings
If you have to input passwords for encrypted disk volumes, you might want to set the keyboard layout and specify a display font.
A typical german kernel command would contain:
LANG=de_DE.UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=de-latin1-nodeadkeys
rhgb表示redhat graphics boot,就是会看到图片来代替启动过程中显示的文本信息,这些信息在启动后用dmesg也可以看到
quiet表示在启动过程中只有重要信息显示,类似硬件自检的消息不回显示crashkernel=auto kdump使用的内存空间
#initrd /initramfs-2.6.32-71.el6.i686.img
boot loader装入kernel,然后kernel需要执行/sbin/init,读取这个文件就必须先mount根文件系统,早期是通过启动时的root="参数"告诉内核根文件系统在哪个设备上,随着硬件和技术的发展,现在根文件系统可能位于一个网络存储如NFS上,可能由于RAID而散布于多个设备上,可能位于一个加密设备上需要提供用户名和密码,这时root="参数"就显得不够了。为了应付这种局面,先后出现两种机制来作为boot loader装载kernel到真正的/sbin/init执行这个启动过程的桥梁: initrd和nitramfs
initrd:
虚拟文件系统(Initial RAM Disk) 一般使用的文件名为 /boot/initrd ,这个文件的作用是,能够通过 boot loader 来加载到内存中, 然后这个文件会被解压缩并且在内存当中仿真成一个根目录, 且此仿真在内存当中的文件系统能够提供一支可执行的程序,通过该程序来加载开机过程中所最需要的内核模块,通常这些模块就是 USB, RAID, LVM,SCSI 等文件系统与磁盘接口的驱动程序。等载入完成后,会帮助内核重新呼叫 /sbin/init 来开始后续的正常开机流程。
Initial RAM Disk是一个基于ram的块设备,因此它占据了一块固定的内存,而且事先要使用特定的工具比如mke2fs格式化,还需要一个文件系统驱动来读写其上的文件。如果这个disk上的空间没有用完,这些未用的内存就浪费掉了,并且这个disk的空间固定导致容量有限,要想装入更多的文件就需要重新格式化。由于Linux的块设备缓冲特性,ram disk上的数据被拷贝到page cache(对于文件数据)和dentry cache(对于目录项),这个也导致内存浪费。
initramfs:
最初的想法是Linus提出的: 把cache当作文件系统装载。 他在一个叫ramfs的cache实现上加了一层很薄的封装,其它内核开发人员编写了一个改进版tmpfs, 这个文件系统上的数据可以写出到交换分区,而且可以设定一个tmpfs装载点的最大尺寸以免耗尽内存。initramfs就是tmpfs的一个应用。Linux 2.6 kernel提出了这种新的实现机制,即
initramfs。顾名思义,initramfs只是一种RAM filesystem而不是disk。
initramfs的优点:
(1)tmpfs随着其中数据的增减自动增减容量。
(2)在tmpfs和page cache/dentry cache之间没有重复数据。
(3)tmpfs重复利用了Linux caching的代码,因此几乎没有增加内核尺寸,而caching的代码已经经过良好测试,所以tmpfs的代码质量也有保证。
(4)不需要额外的文件系统驱动。
另外,initrd机制被设计为旧的"root="机制的前端,而非其替代物,它假设真正的根设备是一个块设备,而且也假设了自己不是真正的根设备,这样不便将NFS等作为根文件系统,最后/linuxrc不是以PID=1执行的,因为1这个进程ID是给/sbin/init保留的。initrd机制找到真正的根设备后将其设备号写入/proc/sys/kernel/real-root-dev,然后控制转移到内核由其装载根文件系统并启动/sbin/init。initramfs则去掉了上述假设,而且/init以PID=1执行,由init装载根文件系统并用exec转到真正的/sbin/init,这样也
导致一个更为干净漂亮的设计。
下面让我们来看看initrd文件的具体内容,
[root@osdba~]# mkdir /tmp/initramfs/
[root@osdba~]# cp /boot/initramfs-2.6.32-71.el6.i686.img /tmp/initramfs/
[root@osdba~]# cd /tmp/initramfs/
[root@osdba initramfs]# file initramfs-2.6.32-71.el6.i686.img
initramfs-2.6.32-71.el6.i686.img: gzip compressed data, from Unix, last modified: Wed Aug 24 23:14:47 2011, max compression
我们可以看到,这个文件是GZIP压缩的文件,我们把后缀改成gz,然后再进行解压
[root@osdba initramfs]# mv initramfs-2.6.32-71.el6.i686.img initramfs-2.6.32-71.el6.i686.img.gz
[root@osdba initramfs]# gunzip initramfs-2.6.32-71.el6.i686.img.gz
解压后,我们再来看看这个文件是什么类型的
[root@osdba initramfs]# file initramfs-2.6.32-71.el6.i686.img
initramfs-2.6.32-71.el6.i686.img: ASCII cpio archive (SVR4 with no CRC)
是cpio压缩成的文件
[root@osdba initramfs]# cpio -iv < initramfs-2.6.32-71.el6.i686.img
[root@osdba initramfs]# ls -l
total 29708
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 bin
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 cmdline
drwxr-xr-x. 3 root root 4096 Sep 19 14:52 dev
-rw-r--r--. 1 root root 18 Sep 19 14:52 dracut-004-32.el6
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 emergency
drwxr-xr-x. 7 root root 4096 Sep 19 14:52 etc
-rwxr-xr-x. 1 root root 8088 Sep 19 14:52 init
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 initqueue
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 initqueue-finished
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 initqueue-settled
-rw-r--r--. 1 root root 30326784 Sep 19 14:57 initramfs-2.6.32-71.el6.i686.img
drwxr-xr-x. 9 root root 4096 Sep 19 14:52 lib
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 mount
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 pre-pivot
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 pre-trigger
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 pre-udev
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 proc
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 sbin
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 sys
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 sysroot
drwxr-xr-x. 2 root root 4096 Sep 19 14:52 tmp
drwxr-xr-x. 6 root root 4096 Sep 19 14:52 usr
drwxr-xr-x. 3 root root 4096 Sep 19 14:52 var
看结果,和我们系统中的/很类似。(有兴趣,慢慢了解)
----------------------------------------------------------------------------------------------------------------
5、在内核加载完毕、进行完硬件检测与驱动程序加载后,此时主机硬件已经准备就绪了,这时候内核会主动的呼叫第一支程序,那就是 /sbin/init。/sbin/init 最主要的功能就是准备软件执行的环境,包括系统的主机名、网络设定、语言、文件系统格式及其他服务的启动等。 而所有的动作都会通过 init的配置文件/etc/inittab来规划,而inittab 内还有一个很重要的设定内容,那就是默认的 runlevel (开机运行级别)。先来看看运行级别Run level,Linux就是通过设定run level来规定系统使用不同的服务来启动,让Linux的使用环境不同。我们来看看这个inittab文件里面的支持级别(RHEL6系统里面的,和以前的其它版本有很大的差别).
[root@osdba~]# cat /etc/inittab
# inittab is only used by upstart for the default runlevel.
#
# ADDING OTHER CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
#
# System initialization is started by /etc/init/rcS.conf
#
# Individual runlevels are started by /etc/init/rc.conf
#
# Ctrl-Alt-Delete is handled by /etc/init/control-alt-delete.conf
#
# Terminal gettys are handled by /etc/init/tty.conf and /etc/init/serial.conf,
# with configuration in /etc/sysconfig/init.
#
# For information on how to write upstart event handlers, or how
# upstart works, see init(5), init(8), and initctl(8).
#
# Default runlevel. The runlevels used are:
# 0 - halt (Do NOT set initdefault to this) (系统直接关机)
# 1 - Single user mode (单用户模式,用在系统出问题时的维护)
# 2 - Multiuser, without NFS (The same as 3, if you do not have networking) (类似底下的 runlevel 3,但无 NFS 服务)
# 3 - Full multiuser mode (完整含有网络功能的字符模式)
# 4 - unused (系统保留,未使用)
# 5 - X11 (与 runlevel 3 类似,但加载使用 X Window)
# 6 - reboot (Do NOT set initdefault to this) (重新启动)
#
id:5:initdefault:
注意:0, 4, 6 不是关机、重新启动就是系统保留的,所以不能将预设的run level设定为这三个值。
RHEL6系统上的这个文件和以前的版本有很大的差别,目前这个文件只能设置运行级别,其它的相关配置文件,在此文件中已经做了说明如:
System initialization is started by /etc/init/rcS.conf
Individual runlevels are started by /etc/init/rc.conf
Ctrl-Alt-Delete is handled by /etc/init/control-alt-delete.conf
Terminal gettys are handled by /etc/init/tty.conf and /etc/init/serial.conf,with configuration in /etc/sysconfig/init.
从这个文件我们已经看出,红帽已经使用新的Upstart启动服务来替换以前的init.在RHEL6的版本中,我们可以把/etc/init/这个目录里面的内容,看成是以前/etc/inittab这个文件里的拆分。
我们可能通过man inittab来查看
DESCRIPTION
The /etc/inittab file was the configuration file used by the original
System V init(8) daemon.
The Upstart init(8) daemon does not use this file, and instead reads
its configuration from files in /etc/init. See init(5) for more
details.
UpStart: 基于事件的启动系统
Sysvinit:基于结果的启动系统
SysVinit守护进程(sysvinit软件包)是一个基于运行级别的系统,它使用运行级别(单用户、多用户以及其他更多级别)和链接(位于/etc /rc?.d目录中,分别链接到/etc/init.d中的init脚本)来启动和关闭系统服务。
Upstart init守护进程(upstart软件包)则是基于事件的系统,它使用事件来启动和关闭系统服务。几个很重要的定义
event: 事件, 是指系统状态的一种改变,这种改变会被通知给init进程。举例来说,boot loader会触发startup事件,系统进入runlevel 2的时候会触发runlevel 2事件,某个文件系统被挂载时会触发path-mounted事件, USB设备的插拔也都会产生相应的事件。这些时间会被通知给init进程,然后init进程来决定系统如何处理这些事件
job: 作业,一项作业是init进程读入的一系列指令。你可以使用initctl start或initctl stop命令来开始或停止某项作业,这是调用作业的一种方式。另一种方式是当系统被告知发生什么事件event后,运行该事件所对应的作业。可用sudo initctl list命令列出所有系统作业的运行状态。
task: 任务, a job that performs its work and returns to a waiting state when it is done. 中文是一种完成指定工作后进去等待状态的作业。
service: 服务,a job that does not normally terminate by itself. 举个例子来说, gettys 是以服务来实现的。 init进程监视所有服务,并在服务失败的时候重启服务。
原有的System V init启动过程的缺点是,它基于包含了大量启动脚本的runlevel目录。而Upstart则是事件驱动型的,因此,它只包含按需启动的脚本,这将使启动过程变得更加迅速。经过良好调优并使用Upstart启动方式的Linux服务器的启动速度要明显快于原有的使用System V init的系统。
为了使Upstart更容易理解,它仍然使用了一个init进程。所以,你仍然可以看到/sbin/init,它是所有服务的根进程。
有一个好消息,那就是RHEL 6对启动过程的改变很少。你还是可以处理那些在目录/etc/init.d中的包含服务脚本的服务,所以runlevel的概念一直存在。因此,在使用yum增加一个服务后,照样可以像以前那样使用chkconfig命令激活它。此外,仍然可以用service命令来启动它。
----------------------------------------------------------------------------------------------------------------
6、System initialization is started by /etc/init/rcS.conf
[root@osdba~]# cat /etc/init/rcS.conf
# Note: there can be no previous runlevel here, if we have one it's bad
# information (we enter rc1 not rcS for maintenance). Run /etc/rc.d/rc
# without information so that it defaults to previous=N runlevel=S.
console output
exec /etc/rc.d/rc.sysinit
----------------------------------------------------------------------------------------------------------------
/etc/rc.d/rc.sysinit 这个文件做了哪些工作:
1、获得网络环境
2、挂载设备
3、开机启动画面Plymouth(取替了过往的 RHGB)
4、判断是否启用SELinux
5、显示于开机过程中的欢迎画面
6、初始化硬件
7、用户自定义模块的加载
8、配置内核的参数
9、设置主机名
10、同步存储器
11、设备映射器及相关的初始化
12、初始化软件磁盘阵列(RAID)
13、初始化 LVM 的文件系统功能
14、检验磁盘文件系统(fsck)
15、磁盘配额(quota)
16、重新以可读写模式挂载系统磁盘
17、更新quota(非必要)
18、启动系统虚拟随机数生成器
19、配置机器(非必要)
20、清除开机过程当中的临时文件
21、创建ICE目录
22、启动交换分区(swap)
23、将开机信息写入/var/log/dmesg文件中
这个文件里面的许多预设配置文件在/etc/sysconfig/这个目录当中,要想更多的系统启动信息,大家可以到/var/log/dmesg文件中查看,也可以用dmesg命令来查看。
----------------------------------------------------------------------------------------------------------------
7、Individual runlevels are started by /etc/init/rc.conf
[root@osdba~]# cat /etc/init/rc.conf
# rc - System V runlevel compatibility
#
# This task runs the old sysv-rc runlevel scripts. It
# is usually started by the telinit compatibility wrapper.
start on runlevel [0123456]
stop on runlevel [!$RUNLEVEL]
task
export RUNLEVEL
console output
exec /etc/rc.d/rc $RUNLEVEL
系统服务的启动经过 /etc/rc.sysinit 的系统模块与相关硬件信息的初始化后,RHEL6系统应该已经能顺利工作了。但还需要启动一些为我们提供服务的服务。这个时候,依据在/etc/inittab里面run level的设定值,就可以来决定启动的服务项目了。大家可以打开/etc/rc这个文件来研究.
以运行级别3为例
[root@osdba~]# ls /etc/rc3.d/
K01certmonger K80sssd S08iptables S25netfs
K01smartd K84wpa_supplicant S10network S26acpid
K02oddjobd K86cgred S11auditd S26haldaemon
K10psacct K87restorecond S12rsyslog S26udev-post
K10saslauthd K88nslcd S13cpuspeed S28autofs
K50dnsmasq K89rdisc S13irqbalance S55sshd
K50netconsole K95cgconfig S13rpcbind S64mysqld
K60nfs K95firstboot S15mdmonitor S80postfix
K69rpcsvcgssd K99stap-server S22messagebus S82abrtd
K73ypbind S00microcode_ctl S23NetworkManager S85httpd
K74nscd S01sysstat S24avahi-daemon S90crond
K74ntpd S02lvm2-monitor S24nfslock S95atd
K75ntpdate S03vmware-tools S24rpcgssd S97rhnsd
K80kdump S08ip6tables S24rpcidmapd S99local
在这个目录下的文件主要具有2个特点:
1、全部以Sxx或Kxx(xx为数字)开头
2、全部是连结文件,连结到/etc/init.d/
现在来说明一下这些的目的
S表示启动服务
K表示停止服务
后面的数字是启动的先后顺序
以S55sshd来举例
[root@osdba~]# ls -l /etc/rc3.d/S55sshd
lrwxrwxrwx. 1 root root 14 Aug 24 23:08 /etc/rc3.d/S55sshd -> ../init.d/sshd
意思就是
/etc/rc3.d/S55sshd = /etc/init.d/sshd start
----------------------------------------------------------------------------------------------------------------
8、用户自定义开机启动脚本
上面讲到的都是一些系统服务,Linux系统容许安装其它的软件来提供服务,那我想要自己安装的服务也要在开机启动,那怎么办,没有关系,找 /etc/rc.local 来完成。这就是我们要讲的用户自定义开机启动脚本。我们只要把想启动的脚本写到这个文件中,开机就能启动了,注意一点,写在这里面的脚本要使用绝对路径。
----------------------------------------------------------------------------------------------------------------
9、在/etc/init/start-ttys.conf中我们可以看到有4个纯文本终端(tty[2-5])。我们可以用[Ctrl+Alt+F2~F5]来切换这些终端。如果要切换到X window终端我们可以用
[Ctrl+Alt+F1]
[root@osdba~]# cat /etc/init/start-ttys.conf
#
# This service starts the configured number of gettys.
start on stopped rc RUNLEVEL=[2345]
env ACTIVE_CONSOLES=/dev/tty[1-6]
env X_TTY=/dev/tty1
task
script
. /etc/sysconfig/init
for tty in $(echo $ACTIVE_CONSOLES) ; do
[ "$RUNLEVEL" = "5" -a "$tty" = "$X_TTY" ] && continue
initctl start tty TTY=$tty
done
end script
注:虚拟机个与真实的环境还是有区别的,有很多快捷键是冲突的,所以这个需要自行实验获得。
----------------------------------------------------------------------------------------------------------------
10、加载终端或X-Window接口在完成了系统所有服务的启动后,接下来Linux就会启动终端或者是X Window来等待使用者登陆了!
----------------------------------------------------------------------------------------------------------------
在文件/etc/sysconfig/init中还有一些额外的配置。在这里,定义了一些参数来决定启动信息的格式。除了那些不很重要的设置,有三行我们需要注意:
AUTOSWAP=no
ACTIVE_CONSOLES=/dev/tty[1-6]
SINGLE=/sbin/sushell
AUTOSWAP:这一行的值你可以设定为Yes,这样可以让你的系统能够自动检测交换分区。使用此选项意味着你再也不必在/etc/fstab中挂载交换分区了。
ACTIVE_CONSOLES:这一行决定了虚拟控制台的创建。在大多数情况下,tty[1-6]工作得很好,同时这个选项也允许您分配更多或者更少的虚拟控制台。但是要注意,不要使用tty [1-8],因为tty7是专门为图形界面预留的。
SINGLE=/sbin/sushell:这一行可以有两个参数:/sbin/sushell(系统默认的参数),它会在启动单用户模式时将你带入一个root的 shell,参数/sbin/sulogin会在单用户模式启动之前弹出一个登录提示,你必须输入root账户的密码才能继续下去。
****************************************************************************************************************
下面是RHEL6上面Upstart大致的一个启动过程:
1.内核启动init
2.系统初始化:(/etc/init/rcS.conf exec /etc/rc.d/rc.sysinit)
3.init找到/etc/inittab文件,确定默认的运行级别(X) (/etc/init/rcS.conf exec telinit $runlevel)
4.触发相应的runlevel事件(/etc/init/rc.conf exec /etc/rc.d/rc $RUNLEVEL)
5.开始运行/etc/rc.d/rc,传入参数X
6./etc/rc.d/rc脚本进行一系列设置,最后运行相应的/etc/rcX.d/中的脚本
7./etc/rcX.d/中的脚本按事先设定的优先级依次启动
8.最后执行/etc/rc.d/rc.local
9.加载终端或X-Window接口
想要了解更多的内容,请大家打开/etc/init/这个目录里面的文件看看。
****************************************************************************************************************
运行级别的切换
通过上面的学习,我们知道run level是在/etc/rc.sysinit 执行完毕之后,而run level 的不同
之处仅是/etc/rc[0-6].d 里面启动的服务不同而已。
如果我们切换run level只需要停止与启动一些服务就OK,那么要如何来实现,其它很简单,用init n(数字)来切换。
[root@osdba~]# runlevel
N 5
5:当前的运行级别
N:在当前运行级别之前运行过什么运行级别,因为例中开机就是运行级别5,之前没有运行过其他运行级别,所以是N
[root@osdba~]# init 3
以上是自己的结合上课时的零星总结,如有错误,望大家指正。
With RHEL6 having been released last November, half a year of aging has given me the opportunity to let some of its new features sink in. Definitely my 2 favorites are the introduction of upstart, to replace the aging Sysv init startup mechanism, and the introduction of advanced resource management with Cgroups.
Upstart has been available in Ubuntu since the release of edgy in 2006, and soon after it got picked up by other distributions as well. Upstart is an event-driven init daemon. As a consequence, unlike the previous requirement of having multiple runlevels, with each service defined to start in a particular sequence, Upstart will only start a service if there is an external event requiring it. i.e. It will only start Bluetooth services if there is Bluetooth hardware detected. This has the benefit that a large part of the service configuration is handled automatically and since only necessary services will run, you will have faster startup times and less resource consumption. Upstart still regards a change in runlevel as an event, so most services will still use their old rc style init scripts. As a consequence you can still use chkconfig to control these startup scripts or use the service command to manipulate these services. But for other functionalities, like setting up serial console, you will have to use an upstart job instead of the old way of doing it through inittab.
One good example is when you want to setup a serial console for qemu if you are testing rhel6 in a KVM virtual machine. By default the upstart script /etc/init/serial.conf will start a serial console if the primary console in grub is set to a serial console. If you want a serial console that is not the primary console, you will have to create your own upstart script:
[root@localhost ~]# cat /etc/init/Stty.conf
stop on runlevel [016]
start on runlevel [345]
respawn
instance /dev/ttyS0
exec /sbin/mingetty /dev/ttyS0
The first 2 lines of the script are called the event definition. Upstart needs to now for each service defined in /etc/init, what event will trigger a change in the state of the service. In this case we tell upstart to start the service in runlevels 3, 4 and 5 and to stop the service in runlevel 0, 1 and 6
The rest of the script is what is called the process definition. Here you define what to run and how to run it. In this case we tell upstart to respawn the getty process if it dies, to ensure that there is only one instance running that uses /dev/ttyS0 and we specify the getty command to run.
Now that we’ve create the upstart service we can manipulate it with the initctl tool:
[root@localhost ~]# initctl start Stty
[root@localhost ~]# initctl status Stty
Stty (/dev/ttyS0) start/running, process 18425
Now that we have a serial console running we can connect to it on the hardware node:
[:root@somehost:/root]$ virsh list
Id Name State
----------------------------------
2 test running
[:root@somehost:/root]$ virsh console 2
The version of upstart in RHEL 6 lacks some of the more recent features introduced in Ubuntu like the ability to schedule cron – like jobs and a replacement for inetd with the socket upstart event. I hope these are backported to RHEL 6 soon.
http://blog.chinaunix.net/u/12679/showart_426217.html
upstart从Ubuntu 6.10开始被引入Ubuntu,它是/sbin/init守护进程(daemon)的代替品,是基于事件的(event-based)。它在启动期间负 责处理任务和服务的启动,系统关闭时关闭它们,并在系统运行时管理它们。我发现upstart的中文使用指南很少,就翻译了部分http: //upstart.ubuntu.com/getting-started.html,希望对大家有些帮助。
1.编写工作(job)文件(工作文件用来指示upstart什么时候该做什么事)
一旦你启动和运行upstart,你会想要编写自己的工作文件。请记住工作文件的格式还未稳定,所以如果你更新了upstart,你可能要修改现有的文件。
/etc/event.d中的文件里定义了一些工作,每个工作的名字就是这个目录里的文件名。这些文件都是普通的文本文件,而且不是可执行的。
工作文件的格式把一个或多个的空格或跳格(tab)当作是空白,这些空白会被跳过,除非这些空白在单引号或双引号中。在引号中允许有换行,不过要在换行前加上反斜杠。注释行由#号起头,直到本行行尾。
①exec和script
所有的工作文件都必须要含有exec节(exec stanza)或者script节(script stanza)。它们指定这个工作运行什么文件。
你可以向exec传送可执行文件的路径和可选的参数。任何特殊字母(比如引号或美元符号'$')将被传送到shell来进行解释。 代码:
exec /bin/foo --opt -xyz foo bar
script则把脚本代码传送给/bin/sh执行。shell的-e选项将被使用,所以任何一个命令执行失败都将终止运行脚本。script节的最后一行为"end script"。 代码:
script
# do some stuff
if [ ... ]; then
...
fi
end script
②启动前运行的脚本和结束后运行的脚本pre-start script and post-stop script
在运行exec或者script指定的程序之前或者之后,你可以运行一些附加的shell代码。附加代码不是用来启动主进程的,实际上他们也不能启动主进程。附加代码是用来准备运行环境和进行清理工作的。
启动前运行的脚本指定主进程运行前所要执行的shell代码,跟script一样,所以任何一个命令执行失败都将终止运行脚本。它也以"end script"作为结束。 代码:
pre-start script
# prepare environment
mkdir -p /var/run/foo
end script
结束后运行的脚本指定主进程终止或者被杀死后运行的shell代码。任何一个命令执行失败都将终止运行脚本。它也以"end script"作为结束。 代码:
post-stop script
# clean up
rm -rf /var/run/foo
end script
③start on和stop on
你的工作现在可以由系统管理员手动的启动和停止,然而,你也想要在事件发生时自动启动和停止一些工作。
upstart发出(emit)的主要事件是startup。这个事件在机器第一次启动(没有可写的文件系统或网络)时发出。如果你使用upstart的工作文件范例,你还会有运行级X事件,X是0到6或者S。工作将与指定运行级的init脚本一起运行。
其他工作可以在它们运行时生成事件。当另一个工作停止后你可以用"stopped 工作名"运行自己的工作。(you can have yours run when another job stops by using stopped job.)另一个有用的工作事件是"started 工作名"。(The other useful job event is started job.)(译者注:这段话的意思是说,一个工作的启动和停止也是事件。)
你可以用start on列出一些事件,表明发出这些事件时你想要启动这个工作,也可以用stop on来指明发出某些事件时你想要停止这个工作。 代码:
start on startup
start on runlevel 2
start on runlevel 3
start on stopped rcS
start on started tty1
(译 者注:不知道是不是水平有限,上面的东西翻译得乱七八糟。其实说白了很简单的,假如一个工作文件包含了上面这段代码,那么这个工作会在 "startup","runlevel 2","runlevel 3","tty1启动","rcS停止"这些事件发生时运行。)
④控制台(console)
通过使用console节,你可以改变设置指定工作将信息输出到哪里,从哪里获得输入。console节你可以指定下列参数之一:output(从 /dev/console获得输入输出);owner(输出和Control-C发送给主进程) ;none(默认参数,输入输出指定到/dev/null)。 代码:
exec echo example
console output
2.工作控制(在终端中对工作进行控制)
①start和stop命令
在终端中运行start和stop命令,可以手动启动和停止工作,这两个命令通常被安装到/sbin。他们的参数是工作名,输出状态的改变 代码:
# start tty1
tty1 (start) running, process 7490 active
# stop tty1
tty1 (stop) running, process 7490 killed
②status命令
可以用status命令查询任意工作的状态。status也被安装在/sbin。它使用工作名作为命令行参数,输出每个工作的状态。 代码:
# status tty1
tty1 (stop) waiting
# start tty1
tty1 (start) running, process 4418
# status tty1
tty1 (start) running, process 4418
输出由以下部分组成:工作名 (最后被启动还是被停止) 当前状态, 进程id。
③initctl list命令
所有工作的列表和它们的状态可以用initctl list命令获得。 代码:
# initctl list
control-alt-delete (stop) waiting
logd (start) running, process 2347
rc-default (stop) waiting
rc0 (stop) waiting
rc0-halt (stop) waiting
rc0-poweroff (stop) waiting
rc1 (stop) waiting
rc2 (stop) waiting
rc3 (stop) waiting
rc4 (stop) waiting
rc5 (stop) waiting
rc6 (stop) waiting
rcS (stop) waiting
rcS-sulogin (stop) waiting
sulogin (stop) waiting
tty1 (start) running, process 4418
tty2 (start) running, process 7367
tty3 (start) running, process 7368
tty4 (start) running, process 7369
tty5 (start) running, process 7370
tty6 (start) running, process 7371
④initctl emit命令
自定义事件可以由initctl emit命令发出。所有由这个事件启动或停止的工作都会被影响到,假设有下面这个工作: 代码:
on bounce
exec echo --Bounced--
console output
下面这个命令会执行这个工作: 代码:
# initctl emit bounce
--Bounced--
事件可以带有参数(在emit命令中传递)和环境变量(使用-e选项)
Ubuntu从6.10开始逐步用Upstart()代替原来的SysVinit进行服务进程的管理。RHEL(CentOS)也都从版本6开始转用Upstart代替以往的init.d/rcX.d的线性启动方式。
SysVinit守护进程(sysvinit软件包)是一个基于运行级别的系统,它使用运行级别(单用户、多用户以及其他更多级别)和链接(位于/etc /rc?.d目录中,分别链接到/etc/init.d中的init脚本)来启动和关闭系统服务。SysV启动是线性、顺序的。一个S20的服务必须要等待S19启动完成才能启动,如果一个启动要花很多时间,那么后面的服务就算完全无关,也必须要等。
UpStart(Upstart init daemon)是基于事件的启动系统,它使用事件来启动和关闭系统服务。Upstart是是并行的,只要事件发生,服务可以并发启动。这种方式无疑要优越得多,因为它可以充分利用现在计算机多核的特点,大大减少启动所需的时间。
Upstart是基于事件的,当系统中的什么情况发生变化时,它会运行某个特定的程序。这里被运行的程序多半是用来启动或终止服务的脚本。这个配置方式和System V在系统进入某个运行级别的时候运行init脚本的链接的概念实际上是非常类似的。只不过Upstart更加灵活一些,Upstart不仅能在运行级别改变的时候启动或终止服务,也能在接收到系统发生其他改变的信息的时候启动或终止服务。这些系统的改变被称为“事件”。例如,当Upstart从udev接收到运行时文件系统加载、打印机安装或其他类似的设备添加或删除的信息,并采取相应的行动。Upstart也可以在系统启动、关闭或某个任务状态改变的时候启动或关闭服务。
一、作业(job)
一个作业(job)是init可以理解的一系列指令。典型的指令包括一个程序(二进制文件或是脚本)和事件的名称。Upstart init daemon会在事件触发的时候运行相应的作业。用户也可以分别手动用initctl start和stop 命令手动启动或终止某项工作。
作业又可以分为任务作业(Task)、服务作业(Services)、抽象作业(Abstact Job)。
任务作业是有确定的生命周期和终止状态的,运行并在执行结束后返回到等待状态的作业。例如:删除一个文件。
服务作业是那些长期运行的进程,例如守护进程,通常不会自己终止。比如数据库、web服务器、ftp服务器等就被实现为服务。
抽象作业是没有exec节或script节的作业,这样的作业仍然可以被启动和终止,但是不会被分配PID。这样作业启动后如果没有被管理员终止,会永久的运行。抽象作业只存在于Upstart自己内部,但有时个它非常有用,例如定义“永久运行”的作业,用来同步等。
作业的10种状态:
waiting: 初始状态。
starting: 作业开始启动。
pre-start: 运行pre-start配置节。
spawned: 运行script或exec节。
post-start: 运行post-start节。
running: 运行完post-start节之后的临时状态,表示作业正在运行(但可能没有关联的PID)。
pre-stop:运行pre-stop节。
stopping:运行完pre-stop节之后的临时状态。
killed: 作业要被终止。
post-stop: 运行post-stop节。
作业的状态可通过inictl status命令输出的中status域来显示给用户。
1 2 | #initctl status mysql mysql start/running, process 875 |
init daemon会监测每个服务的状态,如果服务出现问题会重启服务,在某些事件触发时或手工停止时会杀死服务。
Upstart init daemon只能监测哪些使用exec运行的作业,无法监测使用script…end script运行的作业。也就是说,服务应该使用exec运行,而任务则可以使用任意的方法。
Upstart init守护进程读取/etc/init目录下的作业配置文件,并使用inotify来监控它们的改变。配置文件名必须以.conf结尾,可以放在/etc/init/下的子目录中。每个文件定义一个服务或作业,其名称按路径名来称呼。例如定义在/etc/init/rc-sysinit.conf中的作业就称为rc-sysinit,而定义在/etc/init/net/apache.conf的作业称为net/apache。这些文件必须是纯文本且不可执行的。
二、进程(Process)
Process是由工作(jobs)定义的服务(Services)或者任务(Task),它将被init daemon运行。每个job可以定义一个或者多个不同的process,分别在其生命周期的不同状态运行。除抽象作业(Abstact Job)外的所有作业配置文件都必须要含有exec节(exec stanza)或者script节(script stanza)。它们指定这个工作运行什么文件。
在运行exec或者script指定的程序之前或者之后,你可以运行一些附加的shell代码。附加代码不是用来启动主进程的,实际上他们也不能启动主进程。附加代码是用来准备运行环境和进行清理工作的。启动前运行的脚本指定主进程运行前所要执行的shell代码,跟script一样,所以任何一个命令执行失败都将终止运行脚本。它也以”end script”作为结束。
Process定义如下:
1 | exec COMMAND [ARG]... |
定义作业要运行的主进程,注意若有特殊的字符(如引号或$符)将导致整个命令被传递给Shell来运行。例如exec /usr/sbin/acpid -c $EVENTSDIR -s $SOCKET。
1 2 3 | Scritp ...... exec|end script |
定义Shell脚本来运行指定的主进程,该脚本由sh来执行。Shell的-e选项总是被使用,因此任何命令失败将导致脚本终止。注意作业的主进程只能用exec或script节中的一种来定义,不能同时用exec和script配置节来定义。
1 2 3 | Pre-start ...... exec|end script |
本进程在作业的starting事件完成之后,主进程运行之前执行。通常用来准备相关环境,例如创建必要的目录。
1 2 3 | Post-start ...... exec|end script |
本进程在作业的started事件触发之前,主进程产生之前执行。通常用来发送必要的命令给主进程,或者用来延迟started事件,直到主进程准备好接收客户端的访问。
1 2 3 | Pre-stop ...... exec|end script |
本进程在作业被stop on节中的一个事件停止或被stop命令停止时执行。它在作业的stopping事件之前,及主进程被杀死之前执行。通常用来发送必要的shutdown命令给主进程,或调用不带参数的start命令来取消stop。
1 2 3 | Post-stop ...... exec|end script |
本进程在主进程被杀死之后,作业的stopped事件触发之前执行。通常用来清理相关环境,例如删除临时的目录。
三、事件(event)
事件(event)是指系统状态的一种改变,这种改变会被通知给init进程。init进程可以得到的状态变更信息,几乎系统所有的内部或外部状态变更都可以触发一个事件。比如引导程序会触发启动(startup)事件,系统进入运行级别2会触发运行级别2(runlevel 2)事件,而文件系统加载则会触发路径加载(path-mounted)事件,拔掉或安装一个热插拔或USB设备(如打印机)也会触发一个事件。这些事件会被通知给init进程,然后init进程来决定系统如何处理这些事件。用户还可以通过initctl emit命令来手动触发一个事件。
你可以用start on列出一些事件,表明发出这些事件时你想要启动这个工作,也可以用stop on来指明发出某些事件时你想要停止这个工作。
事件定义格式如下:
1 2 3 4 5 | #定义能导致作业自动启动的事件集。KEY和VALUE指定环境变量及其值 start on Event[[KEY=] Value … and| or …] #定义导致作业自动停止的事件集。 stop on Event[[KEY=] Value … and| or …] |
事件定义示例:
1 2 3 | start on started gdm or started kdm start on device-added SUBSYSTEM=tty DEVPATH=ttyS* start on net-device-added INTERFACE!=lo |
用户也可以自己定义一个事件,并让一个作业被这个事件触发。如下的myjob作业定义文件定义了一个被hithere事件触发的作业:
1 2 3 4 5 6 7 | #cat /etc/init/myjob.conf start on hithere script echo “Hi there, here I am!” > /tmp/myjob.out date >> /tmp/myjob.out end script |
这段代码指定在接收到hithere事件时将触发该作业。然后代码执行指定的操作(将一条消息和日期输出到/tmp/myjob.out 文件)。
用户也可以使用initctl emit命令手动触发事件来执行这个作业。
1 | initctl emit hithere |
Upstart的事件数量是没有限制的,但init守护进程和telinit工具定义了一组常用的标准事件。主要有以下几个:
starting: 当作业被调度并开始运行时,由Upstart触发。
started: 当作业正在运行时被触发。
stopping: 当作业开始终止时被触发。
stopped: 当作业已经完成时(成功或失败)被触发。
当upstart init进程启动时,它会发出startup事件,这将激活实现了System V兼容性的事件和runlevel事件。随着作业的启动和停止,init守护进程将触发starting, started, stopping,stopped事件。另一个核心事件shutdown则是在系统关闭时发出的。其他核心事件包括ctrlaltdel,它说明您按下了Ctrl-Alt-Delete或kbdrequest,它用来说明您按下了Alt-Up(向上箭头)键组合。
Upstart有三种事件类型
Signal Event
非阻塞的即异步的。触发信号型事件会立即返回,调用者继续往下执行。信号型的意思就是广播者并不关心谁会接收它,也不需要等待是否发生某种事情,它只是用来提供信息用作通信。使用带–no-wait选项的initctl emit命令来创建信号型事件。例如initctl emit –no-wait mysignal。注意事件触发的非阻塞特性并不会直接影响那些与此事件有关的作业,它只是影响触发者程序,允许其继续执行,而无需等待任何使用这个事件的作业。作业本身的非阻塞特性则会影响作业自己,它使得作业不能被终止或延迟,不能以任何形式持有触发者的操作。
Method Event
阻塞的即同步的。它通常与Task job结合使用。方法型事件的行为类似于编程语言中的method或function call,调用者需要等待这个工作的完成。例如initctl emit mymethod,这个方法型事件被同步地触发,调用者需要等待直到initctl命令完成。在mymethod事件上启动的任务可能运行成功,也可能失败,假设有一个作业/etc/init/myapp.conf,如下:
Hook Event
阻塞的即同步的。钩子介于信号和方法之间。它是一种通知,表示系统发生了一些改变。不同于信号,钩子型事件的触发者需要等待作业的完成。因此钩子通常用来标志即将发生的改变一些事情。starting和stopping是钩子型事件,被Upstart触发以表明作业即将启动或即将终止。
注意事件与状态是有区别的,虽然Upstart内部使用状态(这些状态可以通过initctl status和list命令显示给用户看),但事件是配置文件指定作业期望行为的一种方式,starting, started, stopping, stopped是事件,不是状态。这些事件在一些特殊的状态转移发生之前触发。例如,starting事件在与此事件相关的作业实际进行运行队列之前被触发。
四、作业生命周期
启动一个作业的流程
1)Upstart把作业的目标从stop改成start。正如目标的名字指示的一样,作业(实例)现在尝试启动。目标可以用initctl list和status命令显示。
2)Upstart触发starting事件,指示作业即将启动。这个事件包括两个环境变量:JOB指定作业名;INSTANCE指定实例名,如果启动单一的实例(没有instance配置节),则实例名为空。
3)starting事件完成。
4)如果pre-start节存在,则产生pre-start进程。如果pre-start失败,Upstart把目标从start改成stop,设置表示失败的变量并触发stopping和stopped事件。
5)Upstart产生主进程。即运行script或exec配置节,如果没有script或exec配置节,则Upstart什么也不做。
6)Upstart确定作业的最终PID,可参考expect fork和expect守护r进程。
7)如果post-start配置节存在,则产生post-start进程。。如果post-start失败,Upstart把目标从start改成stop,设置表示失败的变量并触发stopping和stopped事件。
8)Upstart触发started事件。这个事件包含与starting同样的环境变量。对Service job,当started事件完成后,主进程即完全地运行起来了。如果是Task job,则任务执行完成(成功或失败)。
终止一个作业的流程
1)Upstart把作业的目标从start改为stop。现在作业(实例)尝试终止。
2)如果pre-stop配置节存在,则产生pre-stop进程。如果pre-stop失败,Upstart设置表示失败的变量,并触发stopping和stopped事件。
3)如果作业有script或exec配置节,则终止主进程,首先向主进程发送SIGTERM信号,然后Upstart等待kill timeout秒数(默认为5秒),如果进程仍然在运行,则向进程发送SIGKILL信号,因为进程不能选择忽略此信号,因此能保证进程被终止。
4)Upstart触发stopping事件。这个事件有一系列的相关环境变量,包括:
JOB: 与本事件关联的作业名。
INSTANCE: 实例名。
RESULT: “ok”表示作业正常退出,”failed”表示作业失败,注意退出结果的显示可以用normal exit配置节修改。
PROCESS: 导致作业失败的配置节名称。如果RESULT=ok,则本变量不会被设置。如果设置了,可能值有pre-start, post-start, main(表示script或exec配置节), pre-stop, post-stop, respawn(表示作业产生次数超过了respawn limit配置节设置的限制)。
EXIT_STATUS或EXIT_SIGNAL: 如果作业自己退出则设置EXIT_STATUS,如果由于接收到信号退出则设置EXIT_SIGNAL。如果两个变量都没有设置,则进程在产生的过程中出现了问题(例如指定要运行的命令没有找到)。
5)如果post-stop配置节存在,则生成post-stop进程。如果post-start失败,Upstart设置表示失败的变量并触发stopped事件。
6)Upstart触发stopped事件。当stopped事件完成后,作业即完全终止。stopped事件与stopping事件有相同的环境变量集。
五、initctl(init daemon control tool)
initctl是一个具有root权限的管理员可以使用init进程管理控制工具,可以通过这个工具和Upstart init daemon进行通信。initctl可以用来启动或终止作业、列表作业、以及获取作业的状态、发出事件、重启init 进程等等。比如initctl list让您可以通过标识作业状态来深入了解系统操作,它告诉您目前正在等待哪些服务以及哪些服务目前是活动的。initctl工具还可以显示用于调试而接收的事件。
initctl基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | initctl help 作业 commands: start Start job. stop Stop job. restart Restart job. reload Send HUP signal to job. status Query status of job. list List known jobs. 事件 commands: emit Emit an event. Other commands: reload-configuration Reload the configuration of the init daemon. version Request the version of the init daemon. log-priority Change the minimum priority of log messages from the init daemon help display list of commands |
initctl使用示例
通过initctl手动启动、停止、重启MySQL服务
1 2 3 4 5 6 | #启动MySQL服务 initctl start mysql #停止MySQL服务 initctl stop mysql #重启MySQL服务 initctl restart mysql |
更简单的方法:
1 2 3 4 5 6 | #启动MySQL服务 start mysql #停止MySQL服务 stop mysql #重启MySQL服务 restart mysql |
start、stop和restart工具都是initctl的软链接,会直接运行initctl工具中的对应命令。
采用Upstart做启动管理的系统,服务的启动均依赖于/etc/init/下的每个服务对应的配置文件,通过修改这配置文件,可以更改服务的运行级别。所以即使你将rcX.d下的文件删除,也都不会影响系统的启动。
1 2 3 4 5 6 7 8 | ls /etc/init/ apport.conf failsafe-x.conf module-init-tools.conf mounted-tmp.conf nmbd.conf rc.conf ssh.conf tty6.conf upstart-udev-bridge.conf atd.conf hostname.conf mountall.conf mounted-varrun.conf plymouth.conf rcS.conf tty1.conf udev.conf ureadahead.conf console-setup.conf hwclock.conf mountall-net.conf mysql.conf plymouth-log.conf rc-sysinit.conf tty2.conf udev-finish.conf ureadahead-other.conf control-alt-delete.conf hwclock-save.conf mountall-reboot.conf networking.conf plymouth-splash.conf rsyslog.conf tty3.conf udevmonitor.conf cron.conf irqbalance.conf mountall-shell.conf network-interface.conf plymouth-stop.conf screen-cleanup.conf tty4.conf udevtrigger.conf dmesg.conf mikejobs.conf mounted-dev.conf network-interface-security.conf procps.conf smbd.conf tty5.conf ufw.conf |
以Ubuntu下MySQL服务为例,我们先看看基于UpStart的系统下/etc/init.d/mysql脚本详细信息
1 2 3 | ls /etc/init.d/mysql -l lrwxrwxrwx 1 root root 21 2011-06-14 21:22 /etc/init.d/mysql -> /lib/init/upstart-job* |
这里和以前基于Sysv init的系统有很大的不同,这里不在是启动MySQL的执行脚本,而是指向/lib/init/upstart-job的一个软链接,而upstart-job这个脚本会调用initctl工具启动对应的服务。所以用传统的System V方式删掉/etc/init.d/mysql或/etc/rcX.d中是不会影响到MySQL的开机启动的。正是这个原因,网上很多文章说sysv-rc-conf、update-rc.d,rcconf三个基于以前Sysv init的启动管理工具不会生效的原因。不过这种现象只针对以Upstart工作配置文件为基础的服务。那些还是以SystemV脚本管理的软件包依然可以用上面三个管理工具进行启动行的管理,必定还有很多软件包还未采用Upstart方式进行管理。目前Ubuntu系统也都还兼容System V方式的脚本。
六、Ubuntu下常见系统状态下Upstart调用过程
系统启动
我们以Ubuntu为例子,并从Upstart的视角来阐述。在系统引导时,当initramfs文件系统运行起来时(用于设置RAID、解锁加密的文件系统卷等),将会运行/sbin/init并分配PID为1,这样Upstart接过控制权。在默认运行级别2上的启动流程如下:
1)Upstart执行内部的初始化。
2)Upstart触发一个单一的称为startup的事件,这个事件触发其余的系统初始化过程。
3)init运行一些指定了start on startup的作业。这其中最著名的就是mountall作业,用来挂载硬盘和文件系统。
4)mountall作业依次触发一系列的事件,包括local-filesystems, virtual-filesystems, all-swaps等。当系统设备和挂载点可用时,它运行mountall守护程序来完成挂载硬盘和文件系统的工作。
5)virtual-filesystems事件引发udev作业启动。它运行uded守护程序来管理系统的设备,并监控设备的改变。
6)udev作业引发upstart-udev-bridge作业启动。
7)upstart-udev-bridge作业将会在某个点处触发”net-device-up IFACE=lo”事件,以表示本地网络(例如IPv4的127.0.0.0)可用。
8)在最终的文件系统挂载之后,mountall将会触发filesystem事件。
9)由于rc-sysinit作业中有start on filesystem and net-device-up IFACE=lo节,Upstart将会启动rc-sysinit作业。
10)rc-sysinit作业最后调用telinit命令,格式为telinit “${DEFAULT_RUNLEVEL}”。
11)telinit命令触发runlevel事件,即执行runlevel RUNLEVEL=2 PREVLEVEL=N。注意这就是telinit所做的全部工作,它自己并不会切换运行级别,而通过runlevel程序实现。
12)runlevel事件引发很多其他的Upstart作业启动,包括/etc/init/rc.conf,它用来启动遗留的SystemV init系统。
系统关闭
在系统关闭过程中,有一些重要的事实需要知道:
1)Upstart决不会关闭自己。Upstart会在系统断电时终止,如果它之前终止过,说明是一个bug。
2)Upstart决不会终止没有stop on配置节的作业。
3)Ubuntu既使用Upstart作业,也使用SysV作业。核心的服务由Upstart处理,一些额外的服务可以在遗留的SystemV模式下运行。这主要是为向后兼容,因此在Ubuntu的Universe和Mutiverse软件库中有大量的软件包,为避免更改每个软件包以使它能在Upstart下工作,Upstart允许使用已经存在的SystemV(还包括Debian兼容的)脚本。
关闭系统需要先执行关机动作,例如在图形用户界面中单击”Shut Down…”,运行命令shutdown -h now等。关机的流程如下:
1)假设当前运行级别为2,关机动作将会使Upstart触发runlevel事件,即runlevel RUNLEVEL=0 PREVLEVEL=2。
2)作业/etc/init/rc.conf将被运行。这个作业调用/etc/init.d/rc,并传递新的运行级别(“0“)。
3)SystemV系统调用/etc/rc0.d/中必要的脚本(都是指向/etc/init.d/中脚本的链接),来终止SystemV服务。
4)其中有一个/etc/init.d/sendsigs脚本,这个脚本中有个do_stop()函数,它负责杀死所有没有被终止的进程(包括Upstart进程)。
系统重启
先要执行重启动作,例如在图形界面中单击”Restart…”,运行shutdown -r now或reboot。重启的流程如下:
1)假设当前运行级别为2,重启动作将会使Upstart触发runlevel事件,即 runlevel RUNLEVEL=6 PREVLEVEL=2。
2)作业/etc/init/rc.conf将被运行。这个作业调用/etc/init.d/rc,并传递新的运行级别(“6“)。
3)SystemV系统调用/etc/rc6.d/中必要的脚本(都是指向/etc/init.d/中脚本的链接),来终止SystemV服务。
4)其中有一个/etc/init.d/sendsigs脚本,这个脚本中有个do_stop()函数,它负责杀死所有没有被终止的进程(包括Upstart进程)。
恢复模式
Ubuntu提供了恢复模式以应对系统出现问题的情况。这由friendly-recovery软件包来处理。
七、参考文档
http://www.google.com
http://www.starwd.com/?p=68
http://zhoujin.com/post/154/
http://blog.csdn.net/zhoudaxia/article/details/6666914
http://hi.baidu.com/dillisbest/blog/item/582312c621f544cdd00060a3.html