CentOS系统启动流程


我们学习Linux系统,首先必须要了解其启动的流程顺序。这样当我们遇到系统无法启动的时候就能快速定位出问题的原因,以及如何更好、更快的解决该问题。所以,基础很重要的。

CentOS系统启动流程


1. 系统启动流程

CentOS系统启动流程 - 系统启动流程

1.1 BIOS

BIOS:在 PC 启动的过程中,BIOS 担负着初始化硬件,检测硬件功能,以及引导操作系统的责任。

  • BIOS 历史
    • 在早期,BIOS 程序存放于一个断电后内容不会丢失的只读内存中,系统过电或被重置时,处理器第一条指令的地址会被定位到 BIOS 的内存中,让初始化程序开始运行。
    • 英特尔公司从 2000 年开始,发明了可扩展固件接口(Extensible Firmware Interface),用以规范 BIOS 的开发。而支持 EFI 规范的 BIOS 也被称为 EFI BIOS。之后为了推广 EFI,业界多家著名公司共同成立了统一可扩展固件接口论坛(UEFI Forum),英特尔公司将 EFI 1.1 规范贡献给业界,用以制订新的国际标准 UEFI 规范。
  • BIOS 的功能
    • 当电脑的电源打开,BIOS 就会由主板上的闪存(flash memory)运行,并将芯片组和内存子系统初始化。BIOS 会把自己从闪存中,解压缩到系统的主存并且从那边开始运行。
    • PC 的 BIOS 代码也包含诊断功能,以保证某些重要硬件组件,像是键盘、磁盘设备、输出输入端口等等,可以正常运作且正确地初始化。现代的 BIOS 可以让用户选择由哪个设备启动电脑,如光盘驱动器、硬盘、软盘、USB U 盘等等
  • BIOS 固件
    • 由于 BIOS 与硬件系统集成在一起(将 BIOS 程序指令刻录在 IC 中),所以有时候也被称为固件。
    • 在大约 1990 年 BIOS 是保存在 ROM(只读内存)中而无法被修改。因为 BIOS 的大小和复杂程度随时间不断增加,而且硬件的更新速度加快,令 BIOS 也必须不断更新以支持新硬件,于是 BIOS 就改为存储在 EEPROM 或者闪存中,让用户可以轻易更新 BIOS。然而,不适当的运行或是终止 BIOS 更新可能导致电脑或是设备无法使用。
    • 为了避免 BIOS 损坏,有些新的主板有备份的 BIOS(“双 BIOS”主板)。有些 BIOS 有“启动区块”,属于只读内存的一部分,一开始就会被运行且无法被更新。这个程序会在运行 BIOS 前,验证 BIOS 其他部分是否正确无误(经由检查码,凑杂码等等)。如果启动区块侦测到主要的 BIOS 已损坏,通常会自动由软盘驱动器启动电脑,让用户可以修复或更新 BIOS。一部分主板会在确定 BIOS 已损坏后自动搜索软盘驱动器看看有没有完整的 BIOS 文件。此时用户可以放入存储 BIOS 文件的软盘(例如由网上下载的更新版 BIOS 文件,或是自行备份的 BIOS 文件)。
  • BIOS 与 CMOS 的联系和区别 - 联系
    • CMOS 是计算机上另一个重要的存储器,BIOS 程序的设置结果就保存在 CMOS 中。而且,在 BIOS 程序引导计算机启动后,计算机需要载入 CMOS 中的用户信息和常规设置后才能正常使用。
  • BIOS 与 CMOS 的联系和区别 - 区别
    • BIOS 是存储在唯读记忆体(EEPROM),而 CMOS 为随机存储器(RAM)。
    • BIOS 中存储的是程序,而 CMOS 中存储的是普通信息。
    • EEPROM 即是我们常用的 U 盘和各类存储卡,因此可以更新 BIOS,其内容亦能在断电后保存。
    • CMOS RAM 的内容在断电会消失。所以,把主板的电池拆出,便可重置其内容,拆出电池也会重置时间。

1.1.1 硬件自检

BIOS 程序首先检查,计算机硬件能否满足运行的基本条件,这叫做”硬件自检“(Power-On Self-Test),缩写为POST。如果硬件出现问题,主板会发出不同含义的蜂鸣,启动中止。如果没有问题,屏幕就会显示出 CPU、内存、硬盘等信息。

CentOS系统启动流程 - 硬件自检


1.1.2 启动顺序

硬件自检完成后,BIOS 把控制权转交给下一阶段的启动程序。这时,BIOS 需要知道,”下一阶段的启动程序”具体存放在哪一个设备。也就是说,BIOS 需要有一个外部储存设备的排序,排在前面的设备就是优先转交控制权的设备。这种排序叫做”启动顺序“(Boot Sequence)。打开 BIOS 的操作界面,里面有一项就是”设定启动顺序”

CentOS系统启动流程 - 启动顺序


1.2 主引导记录

主引导记录(Master Boot Record)又叫做主引导扇区,是计算机开机后访问硬盘时所必须要读取的首个扇区,它在硬盘上的三维地址为(柱面,磁头,扇区)=(0,0,1)。

  • 主引导记录意义
    • 主引导扇区记录着硬盘本身的相关信息以及硬盘各个分区的大小及位置信息,是数据信息的重要入口。如果它受到破坏,硬盘上的基本数据结构信息将会丢失,需要用繁琐的方式试探性的重建数据结构信息后才可能重新访问原先的数据。
    • 主引导扇区内的信息可以通过任何一种基于某种操作系统的分区工具软件写入,但和某种操作系统没有特定的关系,即只要创建了有效的主引导记录就可以引导任意一种操作系统(操作系统是创建在高级格式化的硬盘分区之上,是和一定的文件系统相联系的)。
  • 主引导记录大小
    • 对于硬盘而言,一个扇区可能的字节数为 128×2n(n=0,1,2,3)。大多情况下,取 n=2,即一个扇区(sector)的大小为 512 字节。

1.2.1 主引导记录的组成

启动代码

  • 第 1-446 字节
  • 调用操作系统的机器码
  • 硬盘引导程序的主要作用是检查分区表是否正确并且在系统硬件完成自检以后将控制权交给硬盘上的引导程序(如 GNU GRUB)。它不依赖任何操作系统,而且启动代码也是可以改变的,从而能够实现多系统引导。

硬盘分区表

  • 第 447-510 字节
  • 分区表信息
  • 硬盘分区表占据主引导扇区的 64 个字节,可以对四个分区的信息进行描述,其中每个分区的信息占据 16 个字节。

结束标志字

  • 第 511-512 字节
  • 主引导记录签名
  • 结束标志字 55,AA 最后两个字节,是检验主引导记录是否有效的标志。

CentOS系统启动流程 - 硬件自检


1.2.2 分区表剖析

CentOS系统启动流程 - 硬件自检

一个例子:如果某一分区在硬盘分区表的信息如下

80 01 01 00 0B FE BF FC 3F 00 00 00 7E 86 BB 00
  • 80“是一个分区的激活标志,表示系统可引导
  • 01 01 00“表示分区开始的磁头号为 1,开始的扇区号为 1,开始的柱面号为 0
  • 0B“表示分区的系统类型是 FAT32,其他比较常用的有 04(FAT16)、07(NTFS)
  • FE BF FC“表示分区结束的磁头号为 254,分区结束的扇区号为 63、分区结束的柱面号为 764
  • 3F 00 00 00“表示首扇区的相对扇区号为 63(小端序)
  • 7E 86 BB 00“表示总扇区数为 12289662(小端序)

1.2.3 主引导扇区的读取流程

系统开机或者重启

  • (1) BIOS 加电自检,BIOS 执行内存地址为FFFF:0000H处的跳转指令,跳转到固化在 ROM 中的自检程序处,对系统硬件(包括内存)进行检查。
  • (2) 读取主引导记录(MBR)。当 BIOS 检查到硬件正常并与 CMOS 中的设置相符后,按照 CMOS 中对启动设备的设置顺序检测可用的启动设备。BIOS 将相应启动设备的第一个扇区(也就是 MBR 扇区)读入内存地址为0000:7C00H处。
  • (3) 检查0000:7CFEH-0000:7CFFH(MBR 的结束标志位)是否等于55AAH,若不等于则转去尝试其他启动设备,如果没有启动设备满足要求则显示”NO ROM BASIC“然后死机。
  • (4) 当检测到有启动设备满足要求后,BIOS 将控制权交给相应启动设备。启动设备的 MBR 将自己复制到0000:0600H处,然后继续执行。
  • (5) 根据 MBR 中的引导代码启动引导程序。

1.2.4 MBR 分区表与 GPT 分区表

全局唯一标识分区表

  • GUID Partition Table,缩写为 GPT,是一个实体硬盘的分区表的结构布局的标准。它是可扩展固件接口(EFI)标准的一部分,被用于替代 BIOS 系统中的一32bits来存储逻辑块地址和大小信息的主引导记录(MBR)分区表。
  • 对于那些扇区为512 字节的磁盘,MBR 分区表不支持容量大于2.2TB的分区。一些硬盘制造商注意到这个局限性,并且将他们的容量较大的磁盘升级到4KB的扇区,这意味着 MBR 的有效容量上限提升到16 TiB。 这个看似“正确的”解决方案,在临时地降低人们对改进磁盘分配表的需求的同时,也给市场带来关于在有较大的块(block)的设备上从 BIOS 启动时,如何最佳的划分磁盘分区的困惑。
  • GPT 分配64bits给逻辑块地址,因而使得最大分区大小在 264-1 个扇区成为可能。对于每个扇区大小为512 字节的磁盘,那意味着可以有9.4ZB8 ZiB个 512 字节或18,446,744,073,709,551,615(264-1)个扇区 ×512(29)字节每扇区)。

CentOS系统启动流程 - 硬件自检

GPT 的特点

  • GPT 硬盘中,分区表的位置信息储存在 GPT 头中。但出于兼容性考虑,硬盘的第一个扇区仍然用作 MBR,之后才是 GPT 头。
  • 跟现代的 MBR 一样,GPT 也使用逻辑区块地址(LBA)替换了早期的 CHS 寻址方式。传统 MBR 信息存储于 LBA 0,GPT 头存储于 LBA 1,接下来才是分区表本身。

点击这里,查看更多关于 GPT 的内容


1.3 硬盘启动

此后,计算机的控制权就要转交给硬盘的某个分区了,这里又分成三种情况。

1.3.1 卷引导记录

  • 上一节提到,四个主分区里面,只有一个是激活的。计算机会读取激活分区的第一个扇区,叫做”卷引导记录“(Volume boot record,缩写为 VBR)。
  • 卷引导记录“的主要作用是,告诉计算机,操作系统在这个分区里的位置。然后,计算机就会加载操作系统了。

1.3.2 扩展分区和逻辑分区

  • 随着硬盘越来越大,四个主分区已经不够了,需要更多的分区。但是,分区表只有四项,因此规定有且仅有一个区可以被定义成”扩展分区“(Extended partition)。
  • 所谓”扩展分区”,就是指这个区里面又分成多个区。这种分区里面的分区,就叫做”逻辑分区“(logical partition)。
  • 计算机先读取扩展分区的第一个扇区,叫做”扩展引导记录“(Extended boot record,缩写为 EBR)。它里面也包含一张 64 字节的分区表,但是最多只有两项(也就是两个逻辑分区)。
  • 计算机接着读取第二个逻辑分区的第一个扇区,再从里面的分区表中找到第三个逻辑分区的位置,以此类推,直到某个逻辑分区的分区表只包含它自身为止(即只有一个分区项)。因此,扩展分区可以包含无数个逻辑分区。
  • 但是,似乎很少通过这种方式启动操作系统。如果操作系统确实安装在扩展分区,一般采用下一种方式启动。

1.3.3 启动管理器

  • 在这种情况下,计算机读取”主引导记录“前面 446 字节的机器码之后,不再把控制权转交给某一个分区,而是运行事先安装的”启动管理器“(boot loader),由用户选择启动哪一个操作系统。
  • Linux 环境中,目前最流行的启动管理器是grub

CentOS系统启动流程 - 硬件自检

1.4 操作系统

此后,控制权转交给操作系统后,操作系统的内核首先被载入内存。

1.4.1 加载内核

操作系统接管硬件以后,首先读入 /boot 目录下的内核文件。

# ll /boot/
总用量 102681
drwxr-xr-x. 3 root root     1024 12月 20 2011 efi
drwxr-xr-x. 2 root root     1024 4月  12 13:27 grub
-rw-r--r--  1 root root   108876 4月  24 2013 config-2.6.32-358.6.1.el6.i686
-rw-r--r--  1 root root   111791 3月  23 09:05 config-2.6.32-573.22.1.el6.i686
-rw-r--r--  1 root root 13996046 4月  30 2013 initramfs-2.6.32-358.6.1.el6.i686.img
-rw-------  1 root root 22567149 4月  12 13:27 initramfs-2.6.32-573.22.1.el6.i686.img
-rw-r--r--  1 root root   182362 4月  24 2013 symvers-2.6.32-358.6.1.el6.i686.gz
-rw-r--r--  1 root root   202279 3月  23 09:05 symvers-2.6.32-573.22.1.el6.i686.gz
-rw-r--r--  1 root root  1898658 4月  24 2013 System.map-2.6.32-358.6.1.el6.i686
-rw-r--r--  1 root root  2035811 3月  23 09:05 System.map-2.6.32-573.22.1.el6.i686
-rwxr-xr-x  1 root root  3918176 4月  24 2013 vmlinuz-2.6.32-358.6.1.el6.i686
-rwxr-xr-x  1 root root  4087808 3月  23 09:05 vmlinuz-2.6.32-573.22.1.el6.i686

1.4.2 启动初始化进程

  • 内核文件加载以后,就开始运行第一个程序 /sbin/init,它的作用是初始化系统环境。
  • 由于 init 是第一个运行的程序,它的进程编号就是 1。其他所有进程都从它衍生,都是它的子进程。

1.4.3 确定运行级别

  • init进程的一大任务,就是去运行这些开机启动的程序。
  • Linux 允许为不同的场合,分配不同的开机启动程序,这就叫做”运行级别“(runlevel)。也就是说,启动时根据”运行级别”,确定要运行哪些程序。
  • Linux 预置七种运行级别(0-6)。一般来说,0 是关机,1 是单用户模式(也就是维护模式),6 是重启。运行级别 2-5,各个发行版不太一样。
  • init进程首先读取文件 /etc/inittab,它是运行级别的设置文件。
  • 对应运行级别有对应不同的目录,在目录中可以看到,除了第一个文件 README 以外,其他文件名都是”字母S+两位数字+程序名”的形式。字母 S 表示 Start,也就是启动的意思(启动脚本的运行参数为 start),如果这个位置是字母K,就代表 Kill(关闭),即如果从其他运行级别切换过来,需要关闭的程序(启动脚本的运行参数为 stop)。后面的两位数字表示处理顺序,数字越小越早处理,所以第一个启动的程序是 motd,然后是 rpcbing、nfs……数字相同时,则按照程序名的字母顺序启动,所以 rsyslog 会先于 sudo 启动。
  • 这个目录里的所有文件(除了 README),就是启动时要加载的程序。如果想增加或删除某些程序,不建议手动修改 /etc/rcN.d 目录,最好是用一些专门命令进行管理。

1.4.4 加载开机启动程序

  • 七种预设的”运行级别”各自有一个目录,存放需要开机启动的程序。不难想到,如果多个”运行级别”需要启动同一个程序,那么这个程序的启动脚本,就会在每一个目录里都有一个拷贝。这样会造成管理上的困扰:如果要修改启动脚本,岂不是每个目录都要改一遍?
  • Linux 的解决办法,就是七个 /etc/rcN.d 目录里列出的程序,都设为链接文件,指向另外一个目录 /etc/init.d,真正的启动脚本都统一放在这个目录中。init 进程逐一加载开机启动程序,其实就是运行这个目录里的启动脚本。

1.4.5 用户登录

开机启动程序加载完毕以后,就要让用户登录了。一般来说,用户的登录方式有三种。

  • 命令行登录
    • init 进程调用 getty 程序(意为 get teletype),让用户输入用户名和密码。输入完成后,再调用 login 程序,核对密码,系统会再多运行一个身份核对程序 /etc/pam.d/login。如果密码正确,就从文件 /etc/passwd 读取该用户指定的 shell,然后启动这个 shell
  • ssh登录
    • 这时系统调用 sshd 程序,系统会再运行 /etc/pam.d/ssh,取代 gettylogin,然后启动 shell
  • 图形界面登录
    • init进程调用显示管理器,Gnome 图形界面对应的显示管理器为gdm(GNOME Display Manager),然后用户输入用户名和密码。如果密码正确,就读取 /etc/gdm3/Xsession,启动用户的会话。

1.4.6 进入 login shell

登录到系统之后,对应的 shell(Bash),它会读入一系列的配置文件。上一步的三种情况,在这一步的处理,也存在差异。

  • 命令行登录
    • 首先读入 /etc/profile,这是对所有用户都有效的配置;然后依次寻找下面三个文件,这是针对当前用户的配置。
    • 需要注意的是,这三个文件只要有一个存在,就不再读入后面的文件了。比如,要是 ~/.bash_profile 存在,就不会再读入后面两个文件了。
~/.bash_profile
~/.bash_login
~/.profile
  • ssh登录
    • 与第一种情况完全相同。
  • 图形界面登录
    • 只加载  /etc/profile  和  ~/.profile。也就是说,~/.bash_profile  不管有没有,都不会运行。

1.4.7 打开  non-login shell

上一步完成以后,Linux 的启动过程就算结束了,用户已经可以看到命令行提示符或者图形界面了。但是,为了内容的完整,必须再介绍一下这一步。用户进入操作系统以后,常常会再手动开启一个 shell。这个 shell 就叫做  non-login shell,意思是它不同于登录时出现的那个 shell,不读取/etc/profile.profile等配置文件。

non-login shell 的重要性,不仅在于它是用户最常接触的那个 shell,还在于它会读入用户自己的 bash 配置文件  ~/.bashrc。大多数时候,我们对于 bash 的定制,都是写在这个文件里面的。

  • CentOS 已经考虑到这个问题了,请打开文件  ~/.profile,可以看到下面的代码。
# cat ~/.profile
if [ -n "$BASH_VERSION" ]; then
  if [ -f "$HOME/.bashrc" ]; then
  . "$HOME/.bashrc"
  fi
fi
  • 上面代码先判断变量  $BASH_VERSION  是否有值,然后判断主目录下是否存在  .bashrc  文件,如果存在就运行该文件。第三行开头的那个点,是source命令的简写形式,表示运行某个文件,写成”source ~/.bashrc“也是可以的。

  • 因此,只要运行~/.profile文件,~/.bashrc文件就会连带运行。但是上一节的第一种情况提到过,如果存在~/.bash_profile文件,那么有可能不会运行~/.profile文件。解决这个问题很简单,把下面代码写入.bash_profile就行了。

# vim .bash_profile
if [ -f ~/.profile ]; then
  . ~/.profile
fi
  • 这样一来,不管是哪种情况,.bashrc都会执行,用户的设置可以放心地都写入这个文件了。

  • Bash 的设置之所以如此繁琐,是由于历史原因造成的。早期的时候,计算机运行速度很慢,载入配置文件需要很长时间,Bash 的作者只好把配置文件分成了几个部分,阶段性载入。系统的通用设置放在  /etc/profile,用户个人的、需要被所有子进程继承的设置放在.profile,不需要被继承的设置放在.bashrc

  • 顺便提一下,除了 Linux 以外, Mac OS X 使用的 shell 也是 Bash。但是,它只加载.bash_profile,然后在.bash_profile里面调用.bashrc。而且,不管是ssh登录,还是在图形界面里启动shell窗口,都是如此。


2. 系统基础知识

超简洁 Linux 机构

  • Linux = kernel + rootfs

内核设计流派

  • 单内核设计:
    • 典型操作系统:Linux
    • 把所有功能集成于同一个程序,各种功能使用进程来实现
  • 微内核设计:
    • 典型操作系统:Windows、Solaris
    • 每种功能使用一个单独子系统实现
  • 点击这里,查看更多关内核的信息

Linux 内核特点

  • 支持模块化 .ko
  • 支持模块的动态装载和卸载

Linux 内核的组成部分

  • 核心文件
    • /boot/vmlinuz-VERSION-release
    • 内核也被做成了rpm包,这里的release表示 rpm 的版本号
# ll -h /boot/vmlinuz-2.6.32-573.22.1.el6.i686
-rwxr-xr-x 1 root root 3.9M 3月  23 09:05 /boot/vmlinuz-2.6.32-573.22.1.el6.i686
# 发现内核是压缩存放的(bz2格式),大约4M左右大小
# file /boot/vmlinuz-2.6.32-573.22.1.el6.i686
/boot/vmlinuz-2.6.32-573.22.1.el6.i686: Linux kernel x86 boot executable bzImage, version 2.6.32-573.22.1.el6.i686 (mockb, RO-rootFS, swap_dev 0x3, Normal VGA
  • ramdisk
    • CentOS 5 ==> /boot/initrd-VERSION-release.img
    • CentOS 6 ==> /boot/initramfs-VERSION-release.img

CentOS系统启动流程

# ll -h /boot/initramfs-2.6.32-573.22.1.el6.i686.img
-rw------- 1 root root 22M 4月  12 13:27 /boot/initramfs-2.6.32-573.22.1.el6.i686.img
  • 模块文件
    • /lib/modules/VERSION-release
# 模块文件会以内核文件的 VERSION-release 号作为目录存放,其中各个模块之间存在依赖关系
# ls /lib/modules/2.6.32-573.22.1.el6.i686/
build              modules.block    modules.ieee1394map  modules.ofmap     modules.symbols      vdso
extra              modules.ccwmap   modules.inputmap     modules.order     modules.symbols.bin  weak-updates
kernel             modules.dep      modules.isapnpmap    modules.pcimap    modules.usbmap
modules.alias      modules.dep.bin  modules.modesetting  modules.seriomap  source
modules.alias.bin  modules.drm      modules.networking   modules.softdep   updates
# 内核的真正存在位置
# ll /lib/modules/2.6.32-573.22.1.el6.i686/kernel/
总用量 32
drwxr-xr-x  3 root root 4096 4月  12 13:23 arch     # 架构
drwxr-xr-x  3 root root 4096 4月  12 13:23 crypto   # 加密解密
drwxr-xr-x 60 root root 4096 4月  12 13:23 drivers  # 驱动
drwxr-xr-x 29 root root 4096 4月  12 13:23 fs       # 文件系统
drwxr-xr-x  3 root root 4096 4月  12 13:23 kernel   # Kernel自己核心功能
drwxr-xr-x  6 root root 4096 4月  12 13:23 lib      # 库
drwxr-xr-x 28 root root 4096 4月  12 13:23 net      # 网络子系统
drwxr-xr-x  9 root root 4096 4月  12 13:23 sound    # 声音子系统
# 我们虚拟机中使用的网卡驱动就是 e1000
# ll /lib/modules/2.6.32-573.22.1.el6.i686/kernel/drivers/net/e1000
总用量 160
-rwxr--r-- 1 root root 163388 3月  23 09:45 e1000.ko

3. 系统启动流程

系统初始化:

  • POST ==> BootSequence(BIOS) ==> Bootloader(MBR) ==> kernel(ramdisk) ==> rootfs(只读) ==> init

用户空间初始化:

  • /sbin/init ==> (/etc/inittab) ==> 设置默认运行级别 ==> 运行系统初始脚本、完成系统初始化 ==> 关闭对应下需要关闭的服务,启动需要启动服务 ==> 设置登录终端

CentOS 6启动流程:

  • POST ==> Boot Sequence(BIOS) ==> Boot Loader (MBR) ==> Kernel(ramdisk) ==> rootfs ==> switchroot ==> /sbin/init ==>(/etc/inittab, /etc/init/*.conf) ==> 设定默认运行级别 ==> 系统初始化脚本 ==> 关闭或启动对应级别下的服务 ==> 启动终端

CentOS系统启动流程

CentOS系统启动流程


3.1 POST

  • 当我们按下开机的电源后,主板上的芯片组会首先向 CPU 发出一个 reset 指令,之后待芯片组检测到电源供电稳定后便撤去 reset 指令,CPU 就会跳转到 BIOS 中的启动代码位置; BIOS 首先要检测关键设备(如内显、显卡等)是否正常,这个过程就称为 POST(加电后自检)。
  • POST 完成后 BIOS 会调用其它代码来进行完整的硬件检测。

3.2 BOOT Sequence

  • 按次序查找各引导设备,第一个有引导程序的设备即为本次启动用到设备。

3.3 MBR

  • 446bytes: 引导加载程序(boot loader)
  • 64bytes: 分区表
  • 2bytes: 有效性标记

3.4 Boot Loader

  • 提供一个菜单,允许用户选择要启动系统或不同的内核版本;把用户选定的内核装载到内存中的特定空间中,解压、展开,并把系统控制权移交给内核。
  • Boot Loader 分类
    • windows
      • ntloader
    • Linux
      • LILO:LInux LOader
      • GRUB: GRand Uniform Bootloader
        • GRUB 0.X: GRUB Legacy
        • GRUB 1.x: GRUB2
# 这是CentOS6中的grub版本,而在7中使用用C语言完全进行重写的grub
# rpm -q grub
grub-0.97-94.el6_7.1.i686

3.5 GRUB

CentOS系统启动流程

  • 1st stage
    • 第 1 阶段很容易就能加载,因为就在 boot loader 当中,用来帮助我们找到第二阶段的存放位置。
    • MBR 分区中的 boot loader 只有 256 字节,这显然并不能直接驱动我们的内核,其实 MBR 中的 boot loader 只是 Grub Legacy 的 stage1(第一阶段),可以引导到存在 Grub Legacy 分区
  • 1_5 stage
    • 第 1_5 阶段,用来加载第二阶段的文件系统驱动。
    • stage1 并不知道 Grub Legacy 所在分区的驱动,所以在 MBR 后的扇区中有着一些常用的文件系统驱动可供 stage1 引导至 stage2 中。
    • 在系统安装之后就确定了,应该使用/boot/grub/中的那个 1_5 阶段文件系统驱动。
  • 2nd stage
    第 2 阶段才是真正的加载内核的过程。

1st stage

CentOS系统启动流程

2nd stage

CentOS系统启动流程

# ll /boot/grub/
总用量 273
-rw-r--r--. 1 root root     64 12月 20 2011 device.map
-rw-r--r--. 1 root root  13392 12月 20 2011 e2fs_stage1_5
-rw-r--r--. 1 root root  12632 12月 20 2011 fat_stage1_5
-rw-r--r--. 1 root root  11760 12月 20 2011 ffs_stage1_5
-rw-------  1 root root   2311 4月  12 13:27 grub.conf
-rw-r--r--. 1 root root  11768 12月 20 2011 iso9660_stage1_5
-rw-r--r--. 1 root root  13280 12月 20 2011 jfs_stage1_5
lrwxrwxrwx. 1 root root     11 12月 20 2011 menu.lst -> ./grub.conf
-rw-r--r--. 1 root root  11968 12月 20 2011 minix_stage1_5
-rw-r--r--. 1 root root  14424 12月 20 2011 reiserfs_stage1_5
-rw-r--r--  1 root root   1341 11月 15 2010 splash.xpm.gz
-rw-r--r--. 1 root root    512 12月 20 2011 stage1
-rw-r--r--. 1 root root 125744 12月 20 2011 stage2
-rw-r--r--. 1 root root  12036 12月 20 2011 ufs2_stage1_5
-rw-r--r--. 1 root root  11376 12月 20 2011 vstafs_stage1_5
-rw-r--r--. 1 root root  13976 12月 20 2011 xfs_stage1_5

3.6 Kernel

  • 自身初始化

    • 探测可识别到的所有硬件设备
    • 加载硬件驱动程序(有可能会借助于ramdisk加载驱动)
    • 以只读方式挂载根文件系统
    • 运行用户空间的第一个应用程序:/sbin/init
  • init 程序的类型

    • CentOS 5
      • SysV:init
      • 配置文件:/etc/inittab
      • 初始化操作都是借助于脚本来启动服务,所以在启动的时候会有大量的创建和关闭进程操作,并且服务之间有依赖关系。
    • CentOS 6
      • Upstart:init
      • 配置文件:/etc/inittab, /etc/init/*.conf
      • Ubuntu 研发的
      • 兼容 CentOS 5
      • 借助于 D-Bus,使进程基于总线方式的,不用系统完成启动就可以使依赖的程序启动,解决了无法之间的依赖问题。但是,还是通过脚本来启动服务。
    • CentOS 7
      • Systemd
      • 配置文件:/usr/lib/systemd/system, /etc/systemd/system
      • 参考 Mac OSX 的系统启动做的
      • Systemd 能够自己来启动服务,而不需要使用 bash。
      • 在系统初始化的时候,不真正的启动进程而是在第一次使用的时候才启动。
  • ramdisk
    内核中的特性之一:使用缓冲和缓存来回事对磁盘上的文件访问

    • ramdisk ==> ramfs
    • CentOS 5 ==> initrd ==> ramdisk
      • 工具程序:mkinitrd
    • CentOS 6 ==> initramfs ==> ramfs
      • 工具程序:mkinitrd, dracut

CentOS系统启动流程

CentOS系统启动流程


3.7 /sbin/init

CentOS 5

  • 运行级别:为了系统的运行或维护等应用目的而设定;
    7 个级别(0-6)

    • 0:关机
    • 1:单用户模式(root, 无须登录), single, 维护模式;
    • 2: 多用户模式,会启动网络功能,但不会启动 NFS;维护模式;
    • 3:多用户模式,正常模式;文本界面;
    • 4:预留级别;可同 3 级别;
    • 5:多用户模式,正常模式;图形界面;
    • 6:重启
  • 默认级别:3, 5

  • 切换级别:init #

  • 查看级别

    • runlevel
    • who -r
# N表示上次的级别为空,3表示本次启动级别
# runlevel
N 3
  • 配置文件/etc/inittab
    • 格式:id:runlevel:action:process
    • 每一行定义一种 action 以及与之对应的 process
    • action
      • wait: 切换至此级别运行一次
      • respawn:此 process 终止,就重新启动之
      • initdefault:设定默认运行级别,process 省略
      • sysinit:设定系统初始化方式,此处一般为指定/etc/rc.d/rc.sysinit
# cat /etc/inittab
# id可以随意指定,只要不重复就可以;3表示默认启动3级别
id:3:initdefault:
# 在上述脚本运行之后,紧接着CentOS 5/6中运行rc.sysinit这个系统初始化脚本
# 可以使用 cat /etc/rc.d/rc.sysinit来查看详细内容。

si::sysinit:/etc/rc.d/rc.sysinit

/etc/rc.d/rc.sysinit: 系统初始化脚本
(1) 设置主机名
(2) 设置欢迎信息
(3) 激活udev和selinux
(4) 挂载/etc/fstab文件中定义的文件系统
(5) 检测根文件系统,并以读写方式重新挂载根文件系统
(6) 设置系统时钟
(7) 激活swap设备
(8) 根据/etc/sysctl.conf文件设置内核参数
(9) 激活lvm及software raid设备
(10) 加载额外设备的驱动程序
(11) 清理操作
# 之后将执行下列对于级别运行脚本,rc 其实是一个脚本,后面是传入的参数
# 可以通过使用 cat /etc/rc.d/rc 查看
# rc 0 --> 意味着读取/etc/rc.d/rc0.d/
l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
...
l6:6:wait:/etc/rc.d/rc 6
  • /etc/rc.d/rcX.d/中文件内容
    • K* ==> K##*
      • 关闭指定服务;
      • ##运行次序;数字越小,越先运行;
      • 数字越小的服务,通常为依赖到别的服务;
    • S* ==> S##*:
      • 启动指定服务;
      • ##运行次序;数字越小,越先运行
      • 数字越小的服务,通常为被依赖到的服务;
# 可以这么类似的理解为如下脚本
# stop
for Sserver in /etc/rc.d/rcX.d/K*; do
  $srv stop
done
# start
for Kserver in /etc/rc.d/rcX.d/S*; do
  $srv start
done
# 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

# 全都是链接文件,它们将连接到 /etc/rc.d/init.d 目录下
# ls rc3.d
K10saslauthd    K89rdisc         S10network                S25blk-availability
S70xensystem    S99shadowsocks   S80postfix                S90crond
K15svnserve     K92ip6tables     S11auditd                 S25netfs
K50netconsole   K92iptables      S12rsyslog                S26udev-post
K87restorecond  S02lvm2-monitor  S14xe-linux-distribution  S55sshd
S99local

# 查看/etc/rc.d/init.d/network,我们发现# chkconfig: 2345 10 90用来指定其执行先后顺序
# ‘-’表示在所有级别下都不为S,即在所以rcX.d目录下创建一个S开头的链接文件。10表示S,90表示K
# cat /etc/rc.d/init.d/network
#! /bin/bash
# network       Bring up/down networking
# chkconfig: 2345 10 90
  • chkconfig命令
    查看服务在所有级别的启动或关闭设定情形:
    chkconfig [--list] [name]

    • 添加
      SysV 的服务脚本放置于/etc/rc.d/init.d (/etc/init.d)
      chkconfig --add name

    • 删除
      chkconfig --del name

    • 修改指定的链接类型
      chkconfig [--level levels] name <on|off|reset>

      • --level LLLL: 指定要设置的级别,省略时表示 2345

注意:正常启动的 3 级别下,最后启动一个服务S99local没有链接至/etc/rc.d/init.d一个服务脚本,而是指向了/etc/rc.d/rc.local脚本。因此,不便或不需写为服务脚本放置于/etc/rc.d/init.d/目录,且又想开机时自动运行的命令,可直接放置于/etc/rc.d/rc.local文件中。

# 不便使用chkconfig命令控制或不需写为服务脚本放置于`/etc/rc.d/init.d/`目录
# 且又想开机时自动运行的命令,可直接放置于`/etc/rc.d/rc.local`文件中
# ls -l /etc/rc.d/rc3.d/S99local
lrwxrwxrwx 1 root root 11 4月  12 13:23 /etc/rc.d/rc3.d/S99local -> ../rc.local

# ls -l /etc/rc.d/rc3.d/S12rsyslog
lrwxrwxrwx 1 root root 17 4月  12 13:23 /etc/rc.d/rc3.d/S12rsyslog -> ../init.d/rsyslog

# 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
sleep 3
sh /usr/local/mount.sh

# 编辑这两个文件的任意一个文件都是可以,因为为连接指向
# ls -l /etc/rc.local
lrwxrwxrwx 1 root root 13 4月  12 13:23 /etc/rc.local -> rc.d/rc.local
# 测试添加一个测试服务
# vim /etc/rc.d/init.d/testserver

# cat /etc/rc.d/init.d/testserver
#!/bin/bash
# chkconfig: 345 78 23
echo hello

# chmod a+x /etc/rc.d/init.d/testserver
# chkconfig --add testserver
# chkconfig --list testserver

# ls -l /etc/rc.d/rc0.d/K23testserver
  • 启动终端
    • 默认启动 6 个虚拟终端提供登录
    • mingetty 会调用 login 程序
tty1:2345:respawn:/usr/sbin/mingetty tty1
tty2:2345:respawn:/usr/sbin/mingetty tty2
...
tty6:2345:respawn:/usr/sbin/mingetty tty6

CentOS 6

CentOS系统启动流程

  • CentOS 6CentOS 5的启动过程基本上差不多,只有用户初始化的时候,会使用到自己特定的 init 程序 upstart 来完成初始化用户空间。

  • init 程序 upstart 的配置文件

    • /etc/inittab
    • /etc/init/*.conf
      /etc/init/*.conf文件语法 遵循 upstart 配置文件语法格式
  • CentOS 中的/etc/inittab用来设定运行级别的,而 CentOS 6 一般不会使用这个脚本来设定的,只是为了兼容 CentOS 5 而已。其真正的配置文件放在 /etc/init/*.conf,其中将各个任务划分成为不同的配置文件。

# ls /etc/init/
control-alt-delete.conf
plymouth-shutdown.conf
rc.conf
rcS-sulogin.conf
start-ttys.conf
init-system-dbus.conf
prefdm.conf
rcS.conf
serial.conf
tty.conf
kexec-disable.conf
quit-plymouth.conf
rcS-emergency.conf
splash-manager.conf
# cat 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.
#
# Do not edit this file directly. If you want to change the behaviour,
# please create a file rc.override and put your changes there.

start on runlevel [0123456]
stop on runlevel [!$RUNLEVEL]
task

export RUNLEVEL
console output
exec /etc/rc.d/rc $RUNLEVEL
# cat tty.conf
# tty - getty
#
# This service maintains a getty on the specified device.
#
# Do not edit this file directly. If you want to change the behaviour,
# please create a file tty.override and put your changes there.

stop on runlevel [S016]

respawn
instance $TTY
exec /sbin/mingetty $TTY
usage 'tty TTY=/dev/ttyX  - where X is console id'

4. 常见问题纪要

  • 系统启动挂载

最近发现 CentOS7/etc/rc.d/rc.local 不会开机执行,于是认真看了下 /etc/rc.d/rc.local 文件内容就发现了问题的原因了。于是,确认了下 /etc/rc.d/rc.local 的权限,发现默认是没有执行权限的。使用 chmod +x /etc/rc.d/rc.local 命令赋值可执行权限即可正常使用了。从提示信息,可以执行这种方式正在弃用的路上。

# 这种方式正在弃用的路上
$ cat /etc/rc.d/rc.local
#!/bin/bash

# 这个文件是为了兼容性的问题而添加的
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
#
# 强烈建议创建自己的systemd服务或udev规则来在开机时运行脚本而不是使用这个文件
# It is highly advisable to create own systemd services or udev rules
# to run scripts during boot instead of using this file.
#
# 强烈建议创建自己的systemd服务或udev规则来在开机时运行脚本而不是使用这个文件
# In contrast to previous versions due to parallel execution during boot
# this script will NOT be run after all other services.
#
# 请记住,你必须执行“chmod +x /etc/rc.d/rc.local”来确保确保这个脚本在引导时执行
# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure
# that this script will be executed during boot.

touch /var/lock/subsys/local
# 以firewalld.service文件为例说明

# 在/usr/lib/systemd/system目录添加firewalld.service配置文件
[Unit]
Description=firewalld - dynamic firewall daemon
Before=network-pre.target
Wants=network-pre.target
After=dbus.service
After=polkit.service
Conflicts=iptables.service ip6tables.service ebtables.service ipset.service
Documentation=man:firewalld(1)

[Service]
EnvironmentFile=-/etc/sysconfig/firewalld
ExecStart=/usr/sbin/firewalld --nofork --nopid $FIREWALLD_ARGS
ExecReload=/bin/kill -HUP $MAINPID
# supress to log debug and error output also to /var/log/messages
StandardOutput=null
StandardError=null
Type=dbus
BusName=org.fedoraproject.FirewallD1
KillMode=mixed

[Install]
WantedBy=multi-user.target
Alias=dbus-org.fedoraproject.FirewallD1.service

# 相关命令
执行命令 => sudo systemctl enable tomcat.service
启动服务 => sudo systemctl start tomcat.service

文章作者: Escape
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Escape !
  目录