一、linux系统的总体启动流程如下:

   POST --> BIOS(Boot Sequence) --> MBR(bootloader) --> kernel + initramfs(或initrd) --> mount rootfs (ro) --> /sbin/init设定默认运行级别 --> 使用/etc/rc.d/rc.sysinit初始化系统 --> 分别启动并关闭指定服务 --> Ctrl+Alt+Delete组合键 --> 启动字符终端 --> 启动图形终端

linux系统启动流程_第1张图片


二、linux系统启动各阶段详述

1、post:加电自检。通电后,CPU会加载位于CMOS中的BIOS程序,BIOS由一系列的汇编指令组成,它会识别并检测主板上的所有硬件,然后按照BIOS里设置的启动顺序依次寻找可引导设备,第一个有引导程序的设备即为启动PC server所用到的设备


2、MBR:CPU读取可引导设备的第一个扇区即MBR(Master boot record),也称“主引导记录”,大小为512字节,其中存放着:

   引导加载程序(boot loader):446bytes,常用的boot loader有LILO和GRUB,现以GRUB为主;

   分区表:64bytes

   有效性标记:2bytes

  CPU执行boot loader,boot loader选择要启动的内核(在当前磁盘的某或某些分区上)并加载到内存中


3、kernel:内核被加载后,它要做的工作主要有:探测硬件、装载驱动程序、以只读方式装载根文件系统(rootfs)、启动第一个进程/sbin/init。

  这里会有个“鸡生蛋”还是“蛋生鸡”的问题,根文件系统存在于磁盘上,内核要装载根文件系统就需要先能驱动磁盘,即需要先装载驱动程序,而驱动程序又在磁盘上。为解决此问题,boot loader在加载内核的同时也把initramfs(或initrd)加载到内存中(有关initramfs和initrd的详细说明见文末补充部分),它是一个临时的根文件系统,里面包含了启动所必须的驱动模块。内核挂载这个临时根,装载驱动程序,然后释放临时根,挂载实际的根文件系统,并启动第一个进程/sbin/init。

  kernel和initramfs文件位于boot目录下:

   内核:/boot/vmlinux-VERSION-release

   initramfs:/boot/initramfs-VERSION-release.img

[root@node2 ~]# ls /boot
config-2.6.32-431.el6.x86_64  grub                                 lost+found                        System.map-2.6.32-431.el6.x86_64
efi                           initramfs-2.6.32-431.el6.x86_64.img  symvers-2.6.32-431.el6.x86_64.gz  vmlinuz-2.6.32-431.el6.x86_64


4、init:内核初始化的最后一步就是启动pid为1的/sbin/init进程。这个进程是系统的第一个进程,它负责产生其他所有进程。init进程上来首先做的事是去读取/etc/inittab/etc/init/*.conf

  CentOS 5、6、7上的init程序各不相同:

    CentOS 5:传统SysV的init,且只有/etc/inittab 

    CentOS 6:由ubantu开发的upstart,在CentOS 6上也表现为init

          /etc/inittab,/etc/init/*.conf

    CentOS 7:systemd,也表现为init


  在/etc/inittab和/etc/init/*.conf中,大致规定了以下动作:  

    取得runlevel即运行级别

    使用/etc/rc.d/rc.sysinit进行系统初始化

    根据runlevel启动相应的服务并关闭需要停止的服务

    确定Ctrl+Alt+Delete组合键功能

    启动字符终端

    启动图形终端

  系统运行级别

    0:关机

    1:single user mode,单用户模式;直接以root身份登录,无需身份认证,且不会开启网络服务

    2:multi user mode,不支持NFS功能

    3:完全多用户模式,文本接口

    4:未使用,预留级别

    5:完全多用户模式,图形接口

    6:重启

    可在命令行切换级别:init #

[root@node2 ~]# 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)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
# 
id:3:initdefault:  #这里设置的运行级别是3

[root@node2 ~]# ls /etc/init
control-alt-delete.conf  plymouth-shutdown.conf  rc.conf             rcS-sulogin.conf          readahead-disable-services.conf  start-ttys.conf
init-system-dbus.conf    prefdm.conf             rcS.conf            readahead-collector.conf  serial.conf                      tty.conf
kexec-disable.conf       quit-plymouth.conf      rcS-emergency.conf  readahead.conf            splash-manager.conf


  ■补:CentOS 5中的/etc/inittab

    与Centos 6不同,Centos 5上只有/etc/inittab,所有的初始化动作都是在此文件中定义的,此文件中定义的登记项都是以:隔开的四个段,格式如下:

    id:runlevels:action:process  例如si::sysinit:/etc/rc.d/rc.sysinit

    各字段的含义:

      id:登记项的标识符,必须是唯一的

      runlevels:系统的运行级别,表示process的action要在哪个级别下运行,可定义多个级别,各级别间不用分隔符;如果为空,表示在所有的运行级别下运行

      具体的action有:

        respawn:当process终止后马上启动一个新的

        wait:当进入指定的runlevels后process才会启动一次,并且到离开runlevels为止

        initfault:设定默认的运行级别

        sysinit:系统初始化,只有系统开机或重启时process才会被执行一次


5、sysinit:在设定了运行等级后,Linux系统执行的第一个用户层文件就是/etc/rc.d/rc.sysinit脚本程序,它做的工作包括:   

    设定主机名:读取/etc/sysconfig/network文件中的HOSTNAME参数,并以之设定主机名

    打印文本欢迎信息

    激活selinux和udev

    挂载/etc/fstab文件中定义的其它文件系统

    激活swap

    检测根文件系统,并以读写方式重新挂载

    设置系统时钟

    根据/etc/sysctl.conf设置内核参数

    激活LVM和RAID设备

    加载额外设备的驱动程序

    清理操作


6、启动指定的默认级别的默认为启动的服务,停止指定的级别下默认为关闭的服务

  /etc/rc.d/rc#.d (#表示运行级别),该目录下都是符号链接文件,其指向的实际服务脚本位于/etc/rc.d/init.d目录中(该目录有个软链接/etc/init.d)。这些链接文件均以S或K开头:

    S##:启动的服务

    K##:停止的服务

    ##:01-99,数字越小,越优先启动或关闭

[root@node2 ~]# ls /etc/rc.d/
init.d  rc  rc0.d  rc1.d  rc2.d  rc3.d  rc4.d  rc5.d  rc6.d  rc.local  rc.sysinit
[root@node2 ~]# ll /etc/rc.d/rc3.d
total 0
...
lrwxrwxrwx. 1 root root 19 Aug  6 04:38 K75quota_nld -> ../init.d/quota_nld
lrwxrwxrwx. 1 root root 16 Aug  6 04:44 K76ypbind -> ../init.d/ypbind
lrwxrwxrwx. 1 root root 15 Aug  6 04:53 K80kdump -> ../init.d/kdump
lrwxrwxrwx  1 root root 24 Oct 31 07:28 K84wpa_supplicant -> ../init.d/wpa_supplicant
lrwxrwxrwx. 1 root root 21 Aug  6 04:34 K87restorecond -> ../init.d/restorecond
lrwxrwxrwx  1 root root 14 Oct 31 07:28 K88sssd -> ../init.d/sssd
lrwxrwxrwx. 1 root root 15 Aug  6 04:34 K89rdisc -> ../init.d/rdisc
lrwxrwxrwx. 1 root root 14 Aug  6 04:38 K99rngd -> ../init.d/rngd
lrwxrwxrwx. 1 root root 22 Aug  6 04:38 S02lvm2-monitor -> ../init.d/lvm2-monitor
lrwxrwxrwx. 1 root root 19 Aug  6 04:36 S08ip6tables -> ../init.d/ip6tables
...
lrwxrwxrwx. 1 root root 11 Aug  6 04:34 S99local -> ../rc.local  #最后一个启动链接文件指向父目录rc.local
[root@node2 ~]# cat /etc/rc.d/rc.local
#!/bin/sh
#
# 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.

touch /var/lock/subsys/local

  见上方,最后的一个启动链接文件为S99local,它指向/etc/rc.d/rc.local,如果我们不想把某些开机后的操作专门写成脚本,可写入此文件中。/etc/rc.d/rc.local还有一个软链接/etc/rc.local


  若想控制某个脚本是否开机启动,可使用chkconfig命令

  ■chkconfig查看或修改服务随系统启动的启动选项它并不是直接激活或停止一个服务,而只是修改了其符号链接。用法如下:

  ①让指定的服务接受chkconfig命令管理:

   第一步:在位于/etc/rc.d/init.d目录下的服务脚本中添加如下一行或多行注释:

     # chkconfig: LEVEL SPRI KPRI ,例如# chkconfig: - 85 15

     chkconfig后面有三个字段,它们的含义分别为:

        LEVEL:当此服务由chkconfig控制时,默认在哪些运行级是启动的。若默认不在任何运行级启动,以“-”号表示

        SPRI:启动优先级

        KPRI:关闭优先级

    第二步:chkconfig --add SERVICE,该命令会自动在/etc/rc.d/rc#.d目录中创建链接文件

  ②chkconfig --del SERVICE:让指定服务不再接受chkconfig管理,会删除链接文件

  ③chkconfig --list [SERVICE]:显示所有服务或指定服务的开机启动设置

  ④chkconfig [--level ###] SERVICE on/off/reset:若不指定--level选项,on和off操作默认只对运行级2、3、4、5有效,而reset默认对所有运行级有效,reset意为重置,即恢复成服务脚本中的初始设置

[root@node2 ~]# cp testsrv /etc/rc.d/init.d/testsrv  #接受chkconfig管理的服务其服务脚本必须位于init.d目录下
[root@node2 ~]# vim /etc/init.d/testsrv 

#!/bin/bash
#
# chkconfig: 2345 90 10  #添加这一行,表示默认在运行级别为2、3、4、5开机启动
srv=`basename $0`

lockFile="/var/lock/subsys/$srv"

[ $# -lt 1 ] && echo "Usage: $srv {start|stop|restart|status}" && exit 4

[ $UID -ne 0 ] && echo "Only root." && exit 5

if [ "$1" == 'start' ]; then
   if [ -f $lockFile ]; then
        echo "$srv is running."
        exit 7
...
[root@node2 ~]# chkconfig --add testsrv   #让testsrv接受chkconfig命令管理
[root@node2 ~]# ll /etc/rc.d/rc3.d | grep 'testsrv'  #显示已创建了链接文件
lrwxrwxrwx  1 root root 17 Oct 31 08:29 S90testsrv -> ../init.d/testsrv
[root@node2 ~]# chkconfig --list testsrv   #显示各级别开机启动设置
testsrv        	0:off	1:off	2:on	3:on	4:on	5:on	6:off
[root@node2 ~]# chkconfig testsrv off  #默认关闭2-5级别开机启动
[root@node2 ~]# chkconfig --list testsrv
testsrv        	0:off	1:off	2:off	3:off	4:off	5:off	6:off
[root@node2 ~]# chkconfig --level 23 testsrv on  #指定在2、3级别随开机启动
[root@node2 ~]# chkconfig --list testsrv
testsrv        	0:off	1:off	2:on	3:on	4:off	5:off	6:off
[root@node2 ~]# chkconfig testsrv reset  #重置
[root@node2 ~]# chkconfig --list testsrv  #显示已恢复成初始设置
testsrv        	0:off	1:off	2:on	3:on	4:on	5:on	6:off
[root@node2 ~]# chkconfig --del testsrv
[root@node2 ~]# ll /etc/rc.d/rc3.d | grep 'testsvr'
[root@node2 ~]# rm -f /etc/init.d/testsrv

 

7、Ctrl+Alt+Delete组合键

  启动系统服务后就是确定Ctrl+Alt+Delete组合键的功能了,配置文件为/etc/init/control-alt-delete.conf

[root@node2 ~]# cat /etc/init/control-alt-delete.conf 
# control-alt-delete - emergency keypress handling
#
# This task is run whenever the Control-Alt-Delete key combination is
# pressed.  Usually used to shut down the machine.
#
# Do not edit this file directly. If you want to change the behaviour,
# please create a file control-alt-delete.override and put your changes there.

start on control-alt-delete

exec /sbin/shutdown -r now "Control-Alt-Delete pressed"  #Control-Alt-Delete组合键的默认功能为重启系统


三、补充:initrd和initramfs

①initrd全称为initial ram disk,即linux初始RAM磁盘,它被加载到内存中后,会被内核当作block device。因为linux在设计上就会尽可能将读取/写入block device的文件予以cache,这样一来就会有重复的block device和cache资料,会浪费内存。

 为解决此问题,linux 2.6之后引入了initramfs,它是一种ram filesystem,被加载至内存后,会被内核直接挂载并读取,不会再次cache

②initramfs或initrd实际是一个经过cpio和gzip工具处理后的归档压缩文件

 展开initramfs过程如下:

[root@node2 ~]# ls /boot
config-2.6.32-431.el6.x86_64  grub                                 lost+found                        System.map-2.6.32-431.el6.x86_64
efi                           initramfs-2.6.32-431.el6.x86_64.img  symvers-2.6.32-431.el6.x86_64.gz  vmlinuz-2.6.32-431.el6.x86_64
[root@node2 ~]# file /boot/initramfs-2.6.32-431.el6.x86_64.img  #显示initramfs是经过zip压缩的
/boot/initramfs-2.6.32-431.el6.x86_64.img: gzip compressed data, from Unix, last modified: Thu Aug  6 04:42:48 2015, max compression
[root@node2 ~]# cp /boot/initramfs-2.6.32-431.el6.x86_64.img ./initramfs-2.6.32-431.el6.x86_64.img.gz  #要添加后缀后才能解压
[root@node2 ~]# gunzip initramfs-2.6.32-431.el6.x86_64.img.gz #解压
[root@node2 ~]# ls
anaconda-ks.cfg  Downloads                            install.log                      Pictures            samba-3.6.23-20.el6.x86_64.rpm  vmware-tools-distrib
a.txt            epel-release-latest-6.noarch.rpm     install.log.syslog               Public              Templates                       zabbix-2.4
Desktop          ftp-0.17-54.el6.x86_64.rpm           Music                            ramfs               testsrv
Documents        initramfs-2.6.32-431.el6.x86_64.img  mysql-5.1.73-5.el6_6.x86_64.rpm  RPM-GPG-KEY-EPEL-6  Videos
[root@node2 ~]# file initramfs-2.6.32-431.el6.x86_64.img #显示为cpio归档文件
initramfs-2.6.32-431.el6.x86_64.img: ASCII cpio archive (SVR4 with no CRC)
[root@node2 ~]# mkdir ramfs
[root@node2 ~]# cd ramfs
[root@node2 ramfs]# cpio -i < ../initramfs-2.6.32-431.el6.x86_64.img  #展开
97331 blocks
[root@node2 ramfs]# ls
bin      dev                 emergency  init       initqueue-finished  initqueue-timeout  lib64  pre-pivot    pre-udev  sbin  sysroot  usr
cmdline  dracut-004-335.el6  etc        initqueue  initqueue-settled   lib                mount  pre-trigger  proc      sys   tmp      var

③使用工具dracut或mkinitrd创建initramfs

 mkinitrd实际上也是调用dracut的功能,在Centos 6上已被dracut取代 

 用法:

   dracut [-f] [-k DIR] /path/to/initramfs-KERNEL_VERSION.img KRENEL_VERSION     

     -f:覆盖现有的同名initramfs文件

     -k DIR:指定加载内核模块的位置;默认为/lib/modules/KERNEL_VERSION

 示例:针对当前运行内核创建

       dracut /boot/initramfs-$(uname -r).img $(uname -r)

     针对指定内核创建:(要确保能找到对应的内核模块)

      dracut /boot/initramfs-3.10.1.img 3.10.1

[root@node2 boot]# mv initramfs-2.6.32-431.el6.x86_64.img initramfs-2.6.32-431.el6.x86_64.img.back   #备份现有initramfs文件
[root@node2 boot]# dracut initramfs-$(uname -r).img $(uname -r)  #针对当前系统内核创建initramfs
[root@node2 boot]# ls
config-2.6.32-431.el6.x86_64  initramfs-2.6.32-431.el6.x86_64.img       symvers-2.6.32-431.el6.x86_64.gz
efi                           initramfs-2.6.32-431.el6.x86_64.img.back  System.map-2.6.32-431.el6.x86_64
grub                          lost+found                                vmlinuz-2.6.32-431.el6.x86_64