坚韧的日光灯 · 苹果教育优惠启动,部分商品将获赠AirPod ...· 6 天前 · |
气宇轩昂的硬盘 · 破幻的精致——掌阅Ocean4Turbo体验 ...· 2 月前 · |
飞翔的开心果 · 芜湖方特欢乐世界_百度百科· 6 月前 · |
销魂的自行车 · 【预告片|中字】魔法奇缘2:解除魔法 ...· 1 年前 · |
大力的电池 · 食用椰子油综述 (修订版) - 知乎· 1 年前 · |
http://blog.csdn.net/kumu_linux/article/details/7653802
systemd 是Linux下的一种init软件,由Lennart Poettering(英语:Lennart Poettering)带头开发并在LGPL 2.1及后续版本许可证下开源发布。其开发目标是提供更优秀的框架以表示系统服务(英语:Service (systems architecture))间的依赖关系,并以此实现系统初始化时服务的并行启动,同时达到降低Shell的系统开销(英语:Computational overhead)的效果,最终代替现在常用的System V与BSD风格init程序。systemd 是 Linux 下一个与 SysV 和 LSB 初始化脚本兼容的系统和服务管理器。systemd 使用 socket 和 D-Bus 来开启服务,提供基于守护进程的按需启动策略,保留了 Linux cgroups 的进程追踪功能,支持快照和系统状态恢复,维护挂载和自挂载点,实现了各服务间基于从属关系的一个更为精细的逻辑控制,拥有前卫的并行性能。systemd 无需经过任何修改便可以替代 sysvinit 。systemd已纳入众多Linux发行版的软件源中,Fedora 15及后续版本都采用的systemd作为Linux下的默认init程序。(在 Fedora 14 的特性中,systemd 是作为一个技术预览。在 Fedora 15 中替代 Upstart 作为默认管理器,具体可参见fedora官方文档说明 点击打开链接 )
SysVinit 守护进程(sysvinit软件包)是一个基于运行级别的系统,它使用运行级别(单用户、多用户以及其他更多级别)和链接(位于/etc /rc?.d目录中,分别链接到/etc/init.d中的init脚本)来启动和关闭系统服务。Upstart init守护进程(upstart软件包)则是基于事件的系统,它使用事件来启动和关闭系统服务。
以下是两种服务管理的却别和对应的命令.
使某服务自动启动 chkconfig –level 3 sshd on systemctl enable sshd.service 使某服务不自动启动 chkconfig –level 3 sshd off systemctl disable sshd.service 检查服务状态 service sshd status systemctl status sshd.service (服务详细信息) systemctl is-active sshd.service (仅显示是否 Active) 显示所有已启动的服务 chkconfig –list systemctl list-units –type=service 启动某服务 service sshd start systemctl start sshd.service 停止某服务 service sshd stop systemctl stop sshd.service 重启某服务 service sshd restart systemctl restart sshd.service<表参考自http://www.talaland.com/systemd-service-configuration/>
RHEL6 使用新的 Upstart 启动服务替换先前的System V init,Upstart是事件驱动型的,因此,它只包含按需启动的脚本,这将使启动过程变得更加迅速。经过良好调优并使用Upstart启动方式的Linux服务器的启动速度要明显快于原有的使用System V init的系统。 RHEL 6对启动过程的改变相对较少,兼容SysV,所以依然可以处理那些在目录/etc/init.d中包含服务脚本的服务,runlevel的概念也是存在于RHEL6中的<fedora因为使用的是systemd,所以基本上runlevel的概念也就不存在了>。
RHEL6下的相关改变:
/etc/inittab 只用来配置系统默认运行级别,所有先前由/etc/inittab来设定的条目,现在都在/etc/init/目录中以单个文件的形式存在。
/etc/init/rcS.conf 通过启动大部分的基本服务来对系统进行初始化的设定
/etc/init/rc.conf 对启动各自的运行级别(runlevel)的设定
/etc/init/control-alt-delete.conf 定义当用户按“control-alt-delete”三个键时的系统行为
/etc/init/tty.conf、/etc/init/serial.conf 定义系统处理终端登录
/etc/sysconfig/init 中ACTIVE_CONSOLES决定了虚拟控制台的创建,AUTOSWAP是否自动检测交换分区,单用户模式下的root使用的SHELL,默认为/sbin/sushell,另外/sbin/sulogin会在单用户模式启动之前弹出登录提示。
https://wiki.archlinux.org/index.php/systemd_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)
摘自 项目主页 :
systemd 是 Linux 下的一款系统和服务管理器,兼容 SysV 和 LSB 的启动脚本。systemd 的特性有:支持并行化任务;同时采用 socket 式与 D-Bus 总线式激活服务;按需启动守护进程(daemon);利用 Linux 的 cgroups 监视进程;支持快照和系统恢复;维护挂载点和自动挂载点;各服务间基于依赖关系进行精密控制。
http://zh.wikipedia.org/wiki/Systemd
systemd 是 Linux 下的一种 init 软件,由 Lennart Poettering 带头开发,并在 LGPL 2.1及其后续版本许可证下开源发布 [2] [3] 。其开发目标是提供更优秀的 框架 以表示 系统服务 间的依赖关系,并依此实现系统初始化时服务的并行启动,同时达到降低 Shell 的 系统开销 的效果,最终代替现在常用的 System V 与 BSD 风格init程序。
systemd这一名字源于 Unix 中的一个惯例:在Unix中常以“d”作为系统 守护进程 ( 英语 : d aemon ,亦称 后台进程 )的后缀标识。除此以外,systemd亦是借代英文术语 D体系 ,而这一术语即是用于描述一个人具有快速地适应环境并解决困难的能力 [4] 。
与多数发行版使用的System V风格init相比,systemd采用了以下新技术:
从设计构思上说,由于systemd使用了cgroup与fanotify等组件以实现其特性,所以只适用于Linux [5] 。有鉴于此,考虑到 kFreeBSD 分支的软件源无法纳入systemd,为与其他分支保持一致, Debian 开发者尽力避免纳入systemd [6] 。但Lennart Poettering本人对此并不在意,并称”Debian kFreeBSD不过是玩具系统“ [7] 。
systemd已纳入众多 Linux发行版 的软件源中,以下简表:
除此以外,systemd已由Lennart Poettering提请纳入 GNOME 3.2的外部依赖关系列表 [20] ,而这意味着所有使用 GNOME 的发行版都应该使用systemd,最低限度来说也必须将其作为配置选项之一。
https://linuxtoy.org/archives/more-than-upstart-systemd.html
简而言之,systemd是一个init程序。根据之前 Upstart的一些介绍大家应该简要了解了传统Sysvinit系统的不足之处。不过,这次的systemd的设计思想更为超前,也可以说是借鉴了OSX上launchd的思路。 感谢 comicfans44 投稿
作者在其项目的 blog页面 有一篇对于init系统很好的介绍。不过内容超长, 在此摘录一些重要部分翻译如下 ,如有错误还请指正。
init进程由内核启动,是所有其他进程的父进程(也许应该译为母进程?:-)因此比其他进程能够做更多事情,比如在启动过程中加载服务进程。
传统的 sysinit 系统不符合一个有效的、快速的init系统标准: 1. 尽可能启动更少进程 2. 尽可能将更多进程并行启动
条款1意味着除非必须否则不启动。不像syslog和DBus,蓝牙服务仅在蓝牙适配器被插入时才被需要;打印服务仅在打印机连接或程序要打印时才需要;甚至sshd也 不是必须的,管理员常常因几个月才连接一次的ssh而一直运行 sshd 进行监听。 条款2意味着不应该像 sysvinit 一样串行执行启动进程,以最大化CPU和IO带宽的效率。
软硬件的动态改变
现代的操作系统应具备高度的动态特性:不同的程序启动、停止,不同的硬件被添加、移除,因此需要服务在被需要时动态的启动。 大多数当前的系统在尽可能并行化启动过程时仍对一些守护进程的调用保持同步:比如 Avahi 需要DBus,故仅在DBus启动、且 DBus 信号准备完成后 Avahi 才被启动。与之相同的是 libvirtd 和 X11 需要HAL(虽然之后HAL会被移除),在 HAL 启动前,libvirtd 和 X11 不会启动。
并行化Socket服务
以上的同步过程导致了启动过程的串行化。能否摆脱同步与串行化的缺点呢?答案是能够! 首先我们要理解 daemon 对彼此的真正依赖是什么,为什么它们在启动时被延迟了。 对于传统的 Unix daemon,答案是:它们在所需要的服务所提供的 socket 准备好连接之前一直处于等待状态。 常见的是 AF UNIX,但也可能是 AF INET。比如,需要DBus服务的客户程序将在 /var/run/dbus/system bus socket 可以被连接之前等待,syslog 服务的客户程序将在 /dev/log 上等待...而这是它们唯一需求的内容!(意指只要这一条件满足,客户程序即无需等待,不论实际的服务是否已经启动。下文详细介绍)
以上就是等待的真正含义。如果能尽早建立客户程序所必须的 socket 而令客户程序处于等待状态而不是在服务程序完全启动后再启动客户程序,我们就能加快启动进程,进一步并行化进程启动。如何做到这一点?在 Unix 族系统上非常简单:在真正需要启动服务之前先监听其 socket,然后将 socket 传递给 exec() (此处直译,含义不懂),如此,在 init 系统中第一步就可建立所有 daemon 所需的所有 socket,在第二步中一次运行所有的 daemon。如果一个服务需要另一个,但被需者未完全启动号,也完全没有问题在被需者查询 socket,处理请求之前,客户进程将因这个请求而被阻塞,但仅有被这个请求阻塞的客户进程被阻塞。并且服务之间的依赖关系将不再需要(手动)配置:如果一次建立所有服务所必须的所有 socket(及启动所有相关服务),则所有服务的所有需求一定在最后会被满足。
下列所说将是重点:如果同时启动了 syslog 和多个其客户进程(意指syslog尚未启动准备好处理请求),而客户进程开始发送请求,则请求会被写入 /dev/log 的Socket 缓存中。除非缓存填满,否则客户进程无需任何等待即可继续完成其启动过程。当 syslog 启动完全后,处理器所有的消息。另一个例子:DBus与其客户进程一起启动,当同步请求发出但没有接受到预想的回应,客户进程将阻塞。当 DBus 启动完成后处理请求,客户进程继续。
内核的socket 缓存将辅助达成最大的并行化。因为内核完成了同步,不再需要用户空间的任何的额外管理!如果在 daemon 启动前所有必须的 socket 都已经可用,依赖关系就变得多余(至少是次要了)。daemon A 需要另一个 daemon B,简单的连接到B。若B已经启动,A的请求成功。若B未完成启动,假如A发出的不是一个同步请求,甚至无需理会(B没有完全启动)。甚至即使B完全没有执行(比如crash了)时再重启B也为时不晚对于A而言二者没有任何分别。借此可达成最佳的并行化和随意的需求时(on-demand)加载。而且在此基础上能够更加鲁棒。因为 socket 即使在相应服务是暂时不可用(如crash)时也可用,所以客户进程不会丢失任何的请求(request入buffer,待重启服务后处理)
更多有趣之事
首先,这不是一种全新的逻辑,这正像是Apple的launchd系统所做的:OSX 中所有 daemon 所需的 socket 都由 launchd 建立,然后服务本身并行启动,无需手动指定相关的依赖性。这一设计创意独具,也正是OSX急速启动的原因。但这一思想在Apple之外未有任何系统有所体现。
在 launchd 之前,这一思想有更早的体现者:古老的 inetd 就如此工作:socket 被 daemon 建立而实际的服务在daemon通过exec()启动相关进程时传递socket的文件描述符。然而 inetd 所关注的当然不是本地服务,而是网络上的服务。他也不是并行化启动或者隐藏依赖的正确工具。
对于 TCP 的 socket inetd 对于每个进入的连接都建立一个新的 daemon 实例,这意味着每个连接将导致一次进程初始化,这无助于高性能服务器。然而此后inetd支持另一种模式单:当第一个连接到来时,第一个实例(单例)被启动,并将继续接受后续的请求。
并行化Bus服务
Linux上现代的daemon都通过DBus而非socket来交互,现在的问题是,对于这些服务,能否施加与启动传统的、基于socket的服务逻辑相同的并行化?答案是可以,DBus已经提供了所有必要的hook:使用DBus将会在第一次访问时加载服务,并且给予最小的、每请求一个的、消费者与生产者同时启动的同步机制。例如Avahi与CUPS(CUPS需要Avahi进行 mDNS/DNS-SD上打印机扫描)同时启动,仅仅是简单的同时启动二者,若CUPS比Avahi启动快,则DBus将把请求缓存入队列,直到 Avahi服务进行了处理。
所以,总结如下:基于Socket和基于DBus的服务可一次并行启动所有进程,无需任何额外的同步。基于激活的策略还能令我们进行延迟加载服务。如果服务很少被用到,我们可以在第一次被访问时启动,而不是在启动过程中启动。
这太Great了~
并行化文件系统任务
对于当前发行版的启动序列图可以看出,文件系统任务所发生的同步点大于 daemon 启动时的同步点:mount fsck等。现在,启动时很多时间都花费在空闲等待所有fstab 中列举的文件系统被加载、 fsck的过程中,仅在完全完成后启动进程才会继续。如何改进?答案是autofs系统。就如同connect() 调用表示一个服务对另一个服务感兴趣,open() 调用(或相似的调用)表示服务对特定的文件或文件系统有需求。所以,我们应该仅令那些访问暂时还不可用(暂未完成 mount、fsck)文件系统的服务阻塞,这样就改进了并行性。首先加载autofs,然后继续正常的mount过程,当被访问的文件系统未准备好,内核将会缓存请求调用,访问进程将被阻塞,一个未成功的访问仅可能阻塞一个进程。当真正的文件系统加载完成后,启动进程如同正常般完成,没有任何缺失的文件,如此我们能够在所有文件系统都准备好之前就加载服务。
假如有人提出将autofs内置于 init 是不妥当的我并不感觉奇怪,也许这会导致更多 crash。然而经过实验,我可以说这并不正确。使用autofs意味着无需马上提供后台 的真实文件系统,这只会导致访问被延迟。如果进程尝试访问autofs但真实的文件系统很长时间都没能加载,进程将被一个sleep中断上被挂起,意味着你可以安全的cancel 它。如果最后这个文件系统都没能加载,那么令 autofs 返回一个error Code(例如 ENOENT)。所以我认为内置 autofs 到init中是正确的。实验代码显示这一想法在实践中工作的很好。
保持第一个登录的用户的 PID 很小(意即启动进程中少启动进程)
我们可以从MacOS的启动逻辑中学到的另一个内容是:shell脚本是有害的。Shell很容易 hack,但是执行却太慢。传统sysvinit启动逻辑就是围绕shell脚本的一种模式不论是/bin/bash还是其他shell(被用来更快执行shell脚本),最后的结果注定很慢在我的系统中,/etc/init.d中的系统脚本至少调用grep 77次,awk92次,cut23次,sed74次。每次调用时这届进程都被产生一次,库被搜索一次,有些像i18n这样的还会加载更多。这些基于琐碎字串比较的进程导致最终难以置信的慢。只有shell脚本如此工作。除此外shell脚本也非常脆弱,环境变量之类能够彻底改变脚本的行为(behaviour),最终难于检查和控制。
所以在启动过程中摆脱shell脚本!在如此之前先要明确使用 shell脚本的实际用意何在:大体上来说,shell 脚本在完成的是些令人厌烦的工作,用于琐碎的设置和卸载服务。这些内容应该用C来完成,位于分离的可执行程序,或者移至daemon本身,或者交给init。看起来我们还无法马上在启动过程中摆脱脚本,用C重写需要时间,且在有些时候也不行 shell 脚本太过灵活以至于无法脱离,但我们当然能让它们不像现在这样突出(被大量使用)。
简单测量shell 脚本对于启动过程的危害可以从你在系统完全启动后加载的第一个进程的PID看出来:启动、登录、打开终端,输入echo $$,对比Linux和MacOS,如下:Linux PID:1823,MacOS PID:154。自己测去吧。
接下来作者分析了Upstart对于这些问题的一些解决方法以及不足之处,接下来介绍了作者的最终结论: putting it all together:systemd
其特性就是...上文中如此多的翻译已经说明了其核心思路,就不具体介绍其中的详细概念了。 值得一提的是systemd解析传统的SysVinit 脚本,将其依赖关系转化为 其原生的依赖关系,因此兼容 Sysvinit 脚本的同时仍能提供并行性,而 Upstart 无法对传统脚本进行并行化。且可通过DBus界面控制。
目前状态:已经可作为Sysinit或 Upstart的替代来使用,有一个修改过的 Fedora13qemu 镜像可供下载使用。http://surfsite.org/f13- systemd-livecd.torrent,作者秒表计时 Fedora13用Upstart启动27s,systemd24s(从grub到 gdm ,相同系统,相同设置,两次连续的启动过程中取较少者)。
http://zhumeng8337797.blog.163.com/blog/static/10076891420123364928269/
众所周知,/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 跟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 命令手动启动或终止一项工作。工作又可以分为任务和服务。
用户也可以自己定义一个事件,并让一个工作被这个事件触发。如下的 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 事件。
在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
在工作中,常常存在如下需求:
在使用upstart的ubuntu系统中,控制登录的脚本主要是:
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
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.
转自:
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域来显示给用户。
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定义如下:
定义作业要运行的主进程,注意若有特殊的字符(如引号或$符)将导致整个命令被传递给Shell来运行。例如exec /usr/sbin/acpid -c $EVENTSDIR -s $SOCKET。
定义Shell脚本来运行指定的主进程,该脚本由sh来执行。Shell的-e选项总是被使用,因此任何命令失败将导致脚本终止。注意作业的主进程只能用exec或script节中的一种来定义,不能同时用exec和script配置节来定义。
本进程在作业被stop on节中的一个事件停止或被stop命令停止时执行。它在作业的stopping事件之前,及主进程被杀死之前执行。通常用来发送必要的shutdown命令给主进程,或调用不带参数的start命令来取消stop。
本进程在主进程被杀死之后,作业的stopped事件触发之前执行。通常用来清理相关环境,例如删除临时的目录。
三、事件(event)
事件(event)是指系统状态的一种改变,这种改变会被通知给init进程。init进程可以得到的状态变更信息,几乎系统所有的内部或外部状态变更都可以触发一个事件。比如引导程序会触发启动(startup)事件,系统进入运行级别2会触发运行级别2(runlevel 2)事件,而文件系统加载则会触发路径加载(path-mounted)事件,拔掉或安装一个热插拔或USB设备(如打印机)也会触发一个事件。这些事件会被通知给init进程,然后init进程来决定系统如何处理这些事件。用户还可以通过initctl emit命令来手动触发一个事件。
你可以用start on列出一些事件,表明发出这些事件时你想要启动这个工作,也可以用stop on来指明发出某些事件时你想要停止这个工作。
事件定义格式如下:
这段代码指定在接收到hithere事件时将触发该作业。然后代码执行指定的操作(将一条消息和日期输出到/tmp/myjob.out 文件)。
用户也可以使用initctl emit命令手动触发事件来执行这个作业。
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基本用法
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
start、stop和restart工具都是initctl的软链接,会直接运行initctl工具中的对应命令。
采用Upstart做启动管理的系统,服务的启动均依赖于/etc/init/下的每个服务对应的配置文件,通过修改这配置文件,可以更改服务的运行级别。所以即使你将rcX.d下的文件删除,也都不会影响系统的启动。
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
这里和以前基于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