Linux系统的启动流程


之前就有了解过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类系统上都默认安装的有这两个命令)

一些参考链接:

最后附上一个从网上找的我觉得讲得还不错的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 条评论

  1. 博主,我的在我的DO的vps上/etc/rc.local加入了开机启动项。启动不了,是怎么回事,手动运行是没有问题的。

      • 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进行测试。现在成功了!原来是我的命令写法有问题,没有加绝对路径,还有要换行。再次感谢

  2. 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

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注