之前就有了解过Linux系统的启动流程方面的问题,但是在有些关键时候我却忘了o(╯□╰)o,看来还是得在blog里面写篇文章学习、记录一下才能更好的加深记忆,实在记不住的话起码还有个可以参考的地方(主要适用于Debian系列的Linux系统,如:Ubuntu;在本文的最后会有一些RedHat系列Linux系统的相关内容介绍)。
首先,在Linux系统中查看当前的运行级别:
- Ubuntu中,使用 runlevel 命令可以查看当前运行级别
- CentOS中,使用 who -r 命令查看当前运行级别
Linux预置七种运行级别(0-6)。一般来说,0是关机,1是单用户模式(也就是维护模式),6是重启。运行级别2-5,各个发行版不太一样,对于Debian来说,都是同样的多用户模式(也就是正常模式)。(网上说还有一种查看系统当前运行级别的方法是查看文件/etc/inittab的内容,但是在Ubuntu系统[或者说是Debian类系统]上默认是没有这样的文件的)
在我的Ubuntu系统上查看信息如下:
# runlevel
N 2
# who -r
run-level 2 2014-07-26 12:57
#
# ll /etc/init
init/ init.d/ initramfs-tools/
#
# ll /etc/rc
rc0.d/ rc3.d/ rc6.d/ rcS.d/
rc1.d/ rc4.d/ rc.local
rc2.d/ rc5.d/
可以看到我的Ubuntu系统启动时的运行级别为2。
那么,运行级别2有些什么程序呢,系统怎么知道每个级别应该加载哪些程序呢?……回答是每个运行级别在/etc目录下面,都有一个对应的子目录,指定要加载的程序。(这里我想说一说其中的/etc/rcS.d/这个目录,因为在网上搜索到的其他文章中很少有关于这个目录的介绍,而我刚刚在Ubuntu的官方wiki帮助中找到了相关的一些说明,在此记录一下:Init scripts are the scripts located in /etc/init.d. These scripts are part of the bootup sequence of Ubuntu. During boot, they are not called directly, but through a structure of symbolic links which manage the services which are to be started in a particular runlevel. The scripts which are symlinked from /etc/rcS.d are executed first. Then the scripts in /etc/rcN.d/ are executed, with N being the chosen runlevel (default 2). 即,在 /etc/rcS.d/ 目录中的脚本是先于 /etc/rcN.d/ 目录中的脚本执行的)
/etc/rc0.d
/etc/rc1.d
/etc/rc2.d
/etc/rc3.d
/etc/rc4.d
/etc/rc5.d
/etc/rc6.d
上面目录名中的”rc”,表示run command(运行程序),最后的d表示directory(目录)。下面让我们看看在我的 /etc/rc2.d 目录中到底指定了哪些程序。
# ls -l /etc/rc2.d/
README
S20denyhosts
S20mysql
S20nginx
S20php-fpm
S20postfix
S20pptpd
S20supervisor
S20sysstat
S20xl2tpd
S50rsync
S70dns-clean
S70pppd-dns
S75sudo
S99digitalocean
S99grub-common
S99ondemand
S99rc.local
可以看到,除了第一个文件README以外,其他文件名都是”字母S+两位数字+程序名”的形式。字母S表示Start,也就是启动的意思(启动脚本的运行参数为start),如果这个位置是字母K,就代表Kill(关闭),即如果从其他运行级别切换过来,需要关闭的程序(启动脚本的运行参数为stop)。后面的两位数字表示处理顺序,数字越小越早处理,所以第一个启动的程序是denyhosts,然后是mysql、nginx……数字相同时,则按照程序名的字母顺序启动,所以mysql会先于nginx启动。
这个目录里的所有文件(除了README),就是启动时要加载的程序。如果想增加或删除某些程序,不建议手动修改 /etc/rcN.d 目录,最好是用一些专门命令进行管理(参考这里和这里)。
# cat /etc/rc2.d/README #查看/etc/rc2.d/中的说明文件README
The scripts in this directory are executed each time the system enters this runlevel.
The scripts are all symbolic links whose targets are located in /etc/init.d/ .
To disable a service in this runlevel, rename its script in this directory so that the new name begins with a ‘K’ and a two-digit number, and run ‘update-rc.d script defaults’ to reorder the scripts according to dependencies. A warning about the current runlevels being enabled not matching the LSB header in the init.d script will be printed. To re-enable the service, rename the script back to its original name beginning with ‘S’ and run update-rc.d again.
For a more information see /etc/init.d/README.
前面提到,七种(0-6)预设的”运行级别”各自有一个目录,存放需要开机启动的程序。不难想到,如果多个”运行级别”需要启动同一个程序,那么这个程序的启动脚本,就会在每一个目录里都有一个拷贝。这样会造成管理上的困扰:如果要修改启动脚本,岂不是每个目录都要改一遍?
Linux的解决办法,就是七个 /etc/rcN.d 目录里列出的程序,都设为链接文件,指向另外一个目录 /etc/init.d ,真正的启动脚本都统一放在这个目录中。init进程逐一加载开机启动程序,其实就是运行这个目录里的启动脚本。如下所示:
# ls -l /etc/rc2.d/
README
S20denyhosts -> ../init.d/denyhosts
S20mysql -> ../init.d/mysql
S20nginx -> ../init.d/nginx
S20php-fpm -> ../init.d/php-fpm
S20postfix -> ../init.d/postfix
S20pptpd -> ../init.d/pptpd
S20supervisor -> ../init.d/supervisor
S20sysstat -> ../init.d/sysstat
S20xl2tpd -> ../init.d/xl2tpd
S50rsync -> ../init.d/rsync
S70dns-clean -> ../init.d/dns-clean
S70pppd-dns -> ../init.d/pppd-dns
S75sudo -> ../init.d/sudo
S99digitalocean -> ../init.d//rc.digitalocean
S99grub-common -> ../init.d/grub-common
S99ondemand -> ../init.d/ondemand
S99rc.local -> ../init.d/rc.local
这样做的另一个好处,就是如果你要手动关闭或重启某个进程,直接到目录 /etc/init.d 中寻找启动脚本即可。比如,我要重启Apache服务器,就运行下面的命令:
$ sudo /etc/init.d/apache2 restart
我在Ubuntu系统中设置自己安装/指定的程序开机启动的方法就是:把启动程序的命令添加到/etc/rc.d/rc.local文件中,比如下面的是设置开机启动L2TP/IPsec的VPN服务。
# cat /etc/rc.local
#!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will "exit 0" on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. #test -e /etc/ssh/ssh_host_dsa_key || dpkg-reconfigure openssh-server iptables --table nat --append POSTROUTING --jump MASQUERADE /etc/init.d/ipsec restart /usr/bin/l2tpstart /usr/local/sbin/xl2tpd exit 0
在RedHat系列Linux下,比如CentOS,还有一种方法就是通过chkconfig命令添加。
刚想起来在Ubuntu系统下还可以通过update-rc.d这个命令进行开机启动程序的设置,比如:配置Metasploit随系统启动运行(如果你想PostgreSQL和Metasploit在开机时运行,你可以使用update-rc.d启用服务)
update-rc.d postgresql enable update-rc.d metasploit enable
update-rc.d命令的使用说明:
usage: update-rc.d [-n] [-f] <basename> remove
update-rc.d [-n] <basename> defaults [NN | SS KK]
update-rc.d [-n] <basename> start|stop NN runlvl [runlvl] […] .
update-rc.d [-n] <basename> disable|enable [S|2|3|4|5]
-n: not really
-f: force
The disable|enable API is not stable and might change in the future.
总结一下,在Linux系统上设置开机启动的方法有:
1.编辑“/etc/rc.local”文件;
2.使用update-rc.d命令(在RedHat类系统上有chkconfig命令)
3.使用rcconf命令(Debian Runlevel Configuration tool,在RedHat类系统上与其对应的应该是setup/ntsysv命令,但我之前在测试时发现:不是在每个RedHat类系统上都默认安装的有这两个命令)
一些参考链接:
- http://www.debian.org/doc/debian-policy/
- UbuntuBootupHowto – Community Help Wiki
- Linux 的启动流程 – 阮一峰的网络日志
- Manage Linux init or startup scripts
- Linux启动过程详解(inittab、rc.sysinit、rcX.d、rc.local)
最后附上一个从网上找的我觉得讲得还不错的Linux启动流程说明(适用于RedHat系的Linux系统):
启动第一步--加载BIOS
当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它。这是因为BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息、PnP特性等等。在此之后,计算机心里就有谱了,知道应该去读取哪个硬件设备了。
启动第二步--读取MBR
众所周知,硬盘上第0磁道第一个扇区被称为MBR,也就是Master Boot Record,即主引导记录,它的大小是512字节,别看地方不大,可里面却存放了预启动信息、分区表信息。
系统找到BIOS所指定的硬盘的MBR后,就会将其复制到0x7c00地址所在的物理内存中。其实被复制到物理内存的内容就是Boot Loader,而具体到你的电脑,那就是lilo或者grub了。
启动第三步--Boot Loader / Grub
Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核做好一切准备。
Boot Loader有若干种,其中Grub、Lilo和spfdisk是常见的Loader。
我们以Grub为例来讲解吧,毕竟用lilo和spfdisk的人并不多。
系统读取内存中的grub配置信息(一般为menu.lst或grub.lst),并依照此配置信息来启动不同的操作系统。
启动第四步--加载内核
根据grub设定的内核映像所在路径,系统读取内存映像,并进行解压缩操作。此时,屏幕一般会输出“Uncompressing Linux”的提示。当解压缩内核完成后,屏幕输出“OK, booting the kernel”。
系统将解压后的内核放置在内存之中,并调用start_kernel()函数来启动一系列的初始化函数并初始化各种设备,完成Linux核心环境的建立。至此,Linux内核已经建立起来了,基于Linux的程序应该可以正常运行了。
启动第五步--用户层init依据inittab文件来设定运行等级
内核被加载后,第一个运行的程序便是/sbin/init,该文件会读取/etc/inittab文件,并依据此文件来进行初始化工作。
其实/etc/inittab文件最主要的作用就是设定Linux的运行等级,其设定形式是“:id:5:initdefault:”,这就表明Linux需要运行在等级5上。Linux的运行等级设定如下:
0:关机
1:单用户模式(单用户、无网络)
2:无网络支持的多用户模式(多用户、无网络)
3:有网络支持的多用户模式(多用户、有网络)
4:保留,未使用
5:有网络支持有X-Window支持的多用户模式(多用户、有网络、X-Window界面)
6:重新引导系统,即重启
关于/etc/inittab文件的学问,其实还有很多。
启动第六步--init进程执行rc.sysinit
在设定了运行等级后,Linux系统执行的第一个用户层文件就是/etc/rc.d/rc.sysinit脚本程序,它做的工作非常多,包括设定PATH、设定网络配置(/etc/sysconfig/network)、启动swap分区、设定/proc等等。如果你有兴趣,可以到/etc/rc.d中查看一下rc.sysinit文件,里面的脚本够你看几天的。
启动第七步--启动内核模块
具体是依据/etc/modules.conf文件或/etc/modules.d目录下的文件来装载内核模块。
启动第八步--执行不同运行级别的脚本程序(/etc/rc.d/rc $RUNLEVEL # $RUNLEVEL为缺省的运行模式 )
根据运行级别的不同,系统会运行rc0.d到rc6.d中的相应的脚本程序,来完成相应的初始化工作和启动相应的服务。
启动第九步--执行/etc/rc.d/rc.local
你如果打开了此文件,里面有一句话,读过之后,你就会对此命令的作用一目了然:
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don’t
# want to do the full Sys V style init stuff.
rc.local就是在一切初始化工作后,Linux留给用户进行个性化的地方。你可以把你想设置和启动的东西放到这里。
启动第十步--执行/bin/login程序,进入登录状态
此时,系统已经进入到了等待用户输入username和password的时候了,你已经可以用自己的帐号登入系统了。
===
本文参考了如下文章,综合提炼而成:
http://bbs.chinaunix.net/thread-835918-1-1.html
http://hi.baidu.com/fembed/blog/item/b9f0881f51145866f624e4be.html
http://baike.baidu.com/view/9485.htm
启动过程中,用户比较关心的几个配置文件有:
–> inittab (/etc/inittab)
–> rc.sysinit (/etc/rc.d/rc.sysinit)
–> rc $RUNLEVEL 调用rcX.d 文件夹里的文件(/etc/rc.d/rc)(一些软件的服务会注册到rcX.d 文件夹里)
–> rc.local (/etc/rc.d/rc.local)(rc.local就是在一切初始化工作后,Linux留给用户进行个性化的地方,你可以把你想设置开机启动的内容放到这里!)
在Ubuntu系统上rc.local文件位于:/etc/rc.local,并且:
# rc.local
#
# This script is executed at the end of each multiuser runlevel.(运行级别2-5,在各个发行版中的说明/设置都不太一样,对于Debian来说,都是同样的多用户模式(也就是正常模式)。) Make sure that the script will “exit 0” on success or any other value on error.
# In order to enable or disable this script just change the execution bits.
# By default this script does nothing.
《 “Linux系统的启动流程” 》 有 10 条评论
博主,我的在我的DO的vps上/etc/rc.local加入了开机启动项。启动不了,是怎么回事,手动运行是没有问题的。
你好,具体是哪个Linux发行版?不同的发行版在实现上稍有区别。
root@debian:~# cat /etc/issue
Debian GNU/Linux 7 n l
root@debian:~# uname -a
Linux debian 3.2.0-4-amd64 #1 SMP Debian 3.2.78-1 x86_64 GNU/Linux
debian7 x64这个版本。
我想加入的启动项是ss+破解版锐速:
ssserver -p 8989 -k password -d start
/serverspeeder/bin/serverspeeder.sh start
手动可以,加入启动项/etc/rc.local不行。
root@debian:~# runlevel
N 2
帮忙看看,多谢啦~
现在手头上没有Debian的机器做实验,只能根据以往的经验先谈一谈:
1.手动执行没问题
这里指的是手动执行命令还是脚本?是否在不同的目录下测试过?环境变量PATH是否OK?
2.加入启动项/etc/rc.local不行
文件/etc/rc.local是否有可执行权限?
你还可以通过命令「sudo /etc/init.d/rc.local start」进行测试。
暂时只想到这些,还请实际操作一下,如果有任何问题还可以继续交流。后续有时间我会把文章内容更新一下。
多谢~~~按照你的方法,/etc/init.d/rc.local start进行测试。现在成功了!原来是我的命令写法有问题,没有加绝对路径,还有要换行。再次感谢
实现 Linux 内核加载的 bash 脚本
I realized nobody knows how the Linux kernel init (initrd, initramfs, mounts, etc) works, so I rewrote it in bash.
https://t.co/187drobILG
https://gist.github.com/marcan/a2eafd605d3d6ac76eb10a7c64f736c3
在CentOS关机/重启前自动运行指定命令或程序
=CentOS 6=
按照 http://unix.stackexchange.com/questions/48973/execute-a-command-before-shutdown/48974#48974 上面讲的方法,测试失败!
=CentOS 7=
按照 http://unix.stackexchange.com/questions/48973/execute-a-command-before-shutdown/294539#294539 上面讲的方法,测试成功!
http://unix.stackexchange.com/questions/48973/execute-a-command-before-shutdown/
http://stackoverflow.com/questions/18891312/how-to-run-a-shell-script-before-shutdown-on-centos
http://superuser.com/questions/1016827/how-do-i-run-a-script-before-everything-else-on-shutdown-with-systemd
http://unix.stackexchange.com/questions/39226/how-to-run-a-script-with-systemd-right-before-shutdown
http://www.thegeekstuff.com/2011/06/chkconfig-examples/
http://superuser.com/questions/284410/how-do-i-create-a-service-using-chkconfig-in-centos
Linux 系统下 init 进程的前世今生
http://blog.jobbole.com/111610/
http://blog.chinaunix.net/uid-23769728-id-3129443.html
Linux启动过程的6个阶段(启动顺序)(6 Stages of Linux Boot Process)
http://www.thegeekstuff.com/2011/02/Linux-boot-process/
`
BIOS #基本输入输出系统
MBR #主引导记录
GRUB #引导程序(Grand Unified Bootloader)
Kernel #内核
Init #超级进程
Runlevel #运行级别/模式
`
Linux pid 1 和 systemd
http://coolshell.cn/articles/17998.html
计算机是如何启动的?
http://www.ruanyifeng.com/blog/2013/02/booting.html
Linux 的启动流程
http://www.ruanyifeng.com/blog/2013/08/linux_boot_process.html
为什么主引导记录的内存地址是0x7C00?
http://www.ruanyifeng.com/blog/2015/09/0x7c00.html
实现 Linux 内核加载的 bash 脚本(Linux kernel initialization, translated to bash)
https://gist.githubusercontent.com/marcan/a2eafd605d3d6ac76eb10a7c64f736c3/raw/96af72eef2579e3992a83798437e1ef0995c688f/linux.sh
Linux系统启动的标准流程
http://blog.csdn.net/kanghua/article/details/1864009