20170828 系统启动与内核管理 (一)

  • CentOS 6启动流程
  • CentOS 5,6的init启动过程详解
  • grub启动过程详解
  • 启动过程错误修复

一、CentOS 6启动流程

(一)Linux组成

  • linux由kernel和rootfs组成:

    • kernel:进程管理、内存管理、网络管理、驱动程序、文件系统、安全功能
    • rootfs:包含程序和库(glibc)
  • 内核涉及流派:

    • 单内核(monolithic kernel):所有功能集成于一个程序,如linux
    • 微内核(micro kernel):每种功能由单独子系统实现,如Windows
  • linux内核特点:

    • 模块化设计:体现为众多的.ko文件(内核模块文件),实现文件系统、硬件驱动、网络协议等kernel功能
    • 可以动态装载、卸载内核模块
  • linux内核组成

    • 核心文件:/boot/vmlinuz-version-release
    • 辅助的伪根系统ramdisk(系统启动时非常重要):
      /boot/initrd-VERSION-release.img (CentOS 5)
      /boot/initramfs-VERSION-release.img (CentOS 6, 7)
    • 模块文件:
      /lib/modules/version-release目录下

(二)CentOS 6启动流程概述

  • 大致流程:POST加电自检-->MBR引导-->GRUB-->加载内核-->启动init进程-->按照配置文件描述运行相应脚本

  • 第1步 POST(Power-On-Self-Test):加电自检

    运行BIOS的自检程序,完成硬件系统的检测。随后按照BIOS设置的启动顺序查找第一个有引导程序的设备为本次启动设备,将设备管理权限移交给启动设备上的引导程序。

  • 第2步 bootloader:引导程序

    位于MBR的前446字节,允许用户选择启动的系统或内核版本,装载、运行内核并把管理权限移交给内核程序。目前linux使用的主流bootloader为GRUB(CentOS 6使用GRUB 0.9, CentOS 7使用GRUB 2)

  • 第3步 GRUB的分阶段引导

    由于MBR给bootloader预留的空间太狭小,GRUB程序分布在硬盘的多个位置

    • primary bootloader:1st stage(位于MBR的前446字节), 1.5 stage(识别/boot分区类型并加载相应的驱动)

    • secondary bootloader:2nd stage(位于/boot/grub,显示启动菜单和用户交互程序,负责内核加载,并将管理权移交给内核)

  • 第4步 内核自身初始化

    • 探测硬件设备

    • 在ramdisk提供基本驱动(只用于满足启动使用)的前提下,加载各硬件驱动

      • ramdisk管理工具:mkinitrd(CentOS 6), dracut(CentOS 7)
    • 以只读方式挂载根文件系统

  • 实验:为当前正在使用的内核重新制作启动文件

mkinitrd /boot/initramfs-`uname -r`.img `uname -r`
或者
dracut /boot/initramfs-`uname -r`.img `uname -r`

删除/boot目录下的initramfs文件

重启后进入系统失败,进入救援模式,先使用chroot命令切换根目录,重建initramfs文件,重启后正常进入系统

  • 第5步 运行用户空间第一个应用程序/sbin/init,启动相关服务

二、CentOS 5,6的init启动过程详解

(一)init程序类型

启动程序 进程名称 应用版本 配置文件
SysV init CentOS 5之前 /etc/inittab
Upstart init CentOS 6 /etc/inittab, /etc/init/*.conf
Systemd systemd CentOS 7 /usr/lib/systemd/system, /etc/systemd/system

(二)运行级别

  • 定义:为系统运行和维护目的而设定,共有0-6共7个级别

    • 0:关机
    • 1:单用户模式,root自动登录,可用于重置root密码
    • 2:不完全的多用户模式,启用网络功能,但不启用NFS
    • 3:完全的多用户模式
    • 4:预留
    • 5:图形界面模式
    • 6:重启
  • 默认的运行级别:/etc/inittab中设置,一般设为3或者5,不能设置为0或者6

  • 修改运行级别:init LEVEL,LEVEL取值为0-6

  • 查看运行级别:runlevelwho -r

  • 实验:忘记root密码时重置root密码
    启动时,在bootloader启动倒计时键盘任意键进入启动项管理页面

按提示输入a,在提示的画面字符串后输入1,代表进入runlevel: 1的单用户模式

无需输入密码,即可进入root账户,此时可以重置root密码

(三)CentOS 5和6的/etc/inittab文件

  • CentOS 5的/etc/inittab文件:

    • 内容:文件的每一行定义一个action和与其对应的process
    • 格式: id:runlevel:action:process
      说明:
      id:仅起到标识作用
      runlevel:运行级别
      action
      (1) wait:切换至此级别运行一次
      (2) respawn:若process终止,则重新运行它,一般在mingetty时使用
      如:1:2345:respawn:/usr/sbin/mingettytty1,这样结束mingetty1进程后会自动产生新的mingetty进程,保证有足够终端供人使用
      (3) initdefault:设定为默认运行级别,process省略
      (4) sysinit:设定系统初始化方式,一般process设为/etc/rc.d/rc.sysinit
  • CentOS 6的/etc/inittab文件和相关文件
    CentOS 6的/etc/inittab文件只保留了设定默认运行级别的语句,其他内容被移动到/etc/init/*.conf各类设置文件中

(四)CentOS 5的init启动流程

(1)运行/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) 清理操作
(2)按照/etc/inittab文件规定的默认运行级别,运行相应级别的/etc/rc.d/rcN.d(N为运行级别)目录下的文件
  • /etc/rc.d/rcN.d文件格式:

    • rcN.d下的文件均为指向/etc/rc.d/init.d目录下服务的软链接文件
    • 软链接的命名格式:K##或者S##
    • S表示运行时开启服务,K表示运行时关闭服务
    • ##代表运行顺序,排序越靠前,越先运行。排序方式为依次对比相同位置的数字字母,数字小于字母
      如:100比99靠前,因为从第1位比较1比9靠前
      又如:99比9a靠前,因为第1位相同,比较第2位9比a靠前
    • 以K开头的服务越靠前,代表服务越依赖于其他服务,因为只有先停止本服务,被依赖的服务才能停止
    • 以S开头的服务越靠前,代表服务越被其他服务所依赖,因为只有先开启本服务,依赖其运行的服务才能开启
    • /etc/rc.d/rc{2,3,4,5}.d目录下最后启动的服务均为S99local,此链接文件链指向/etc/rc.d/rc.local脚本,rc.local脚本不是服务。当需要开机执行一些指令,又不想在/etc/rc.d/init.d目录下专门写一个服务脚本时,可以在本脚本文件中定义。
  • chkconfig命令:设置服务在所有运行级别的开启、关闭情况,语法设置如下:

chkconfig [--list] [service_name]     //查询服务
chkconfig --add service_name          //添加服务
chkconfig --del service_name          //删除服务
chkconfig --levle LLLL sevice_name <on|off|reset>
//修改服务,指定的运行级别--level LLLL省略时默认为2345

chkconfig实质是修改/etc/rc.d/rcN.d目录下文件前缀K或S,如下图所示,关闭atd在runlevel 5级别下启动后,在/etc/rc.d/rc5.d目录下以S开头的atd文件消失,而以K开头的atd文件出现,同时可以看到S靠后的服务往往K靠前,反之亦然,证明先启动的服务需要后退出,而后启动的服务需要先退出

删除/etc/rc.d/rc5.d下的K05atd软链接后,手动建立S95atd软链接,使用chkconfig查询发现已经变成了on,进一步印证chkconfig的工作原理

  • ntsysv:改变运行级别服务的开启、关闭情况
    ntsysv [--level=RUN_LEVEL],默认修改当前运行级别
  • 实验:创建一个自定义服务S97serv,添加、修改、删除服务

第1步,在/etc/rc.d/init.d目录下,新建服务脚本文件

#! /bin/bash
# checkconfig: 35 96 07  //表示在runlevel=3, 5时开启服务,在/etc/rc.d/rc3.d和/etc/rc.d/rc5.d目录下的软链接文件名前缀为S96和K07
# description:   //本描述内容在CentOS 5中必须有,在CentOS 6中可以有
脚本内容略

第2步,chkconfig命令添加此服务,查询此服务在各个运行级别的开启、关闭状态是否符合服务脚本文件中的描述

第3步,chkconfig修改此服务在运行级别3的状态为off

第4步,chkconfig删除此服务

  • service命令:手动管理服务

    • 语法:
      service service_name start|stop|restart|status,服务开启、关闭、重启、状态查询
      service --status-all,查询所有服务的状态
  • xinetd管理的服务:

    • xinetd管理非独立服务(又称瞬态服务),这些服务的使用率不高,为了节省资源占用,平时xinetd服务负责监听这些服务的端口,一旦xinetd服务发现其所管理服务的请求,立即唤醒相应的服务,并将相应服务的端口移交给服务
    • xinetd管理很多服务,所以又称其为超级守护进程
    • 非独立服务依赖于xinetd服务,若xinetd服务尚未安装,则安装非独立服务时xinetd作为依赖服务将一并安装
    • 配置文件:/etc/xinetd.conf, /etc/xinetd.d/<service>
  • 实验:开启Telnet服务
    第1步,chkconfig --list命令发现telnet服务属于非独立服务,需要修改xinetd服务的配置文件

    第2步,配置/etc/xinetd.d/telnet,将disabled的值由yes改为no,重新执行chkconfig --list命令,发现telnet的状态已经变成yes

    第3步,重启xinetd服务,此时通过netstat -ntlp查看监听端口,发现xinetd正在监听telnet的23端口

    第4步,远程telnet登录本机,此时通过ss -ntup查看tcp端口,发现telnet正在23端口处于连接状态

(3)按照/etc/inittab文件的规定,设置登录终端

CentOS 5 init启动顺序总结:

  • 运行/sbin/init,创建用户空间第一个进程-->
  • 查询/etc/inittab初始化配置文件,设置默认运行级别-->
  • 运行/etc/rc.d/rc.sysinit系统初始脚本、完成系统初始化-->
  • 关闭(可能出现)对应下需要关闭的服务,启动需要启动服务/etc/rc.d/rcN.d-->
  • 设置登录终端
  • CentOS 6的启动过程与CentOS 5基本相似,区别在于CentOS 6的init启动过程配置文件为/etc/inittab和/etc/init/*.conf,遵循upstart程序规定的配置文件语法格式

三、grub启动过程详解

(一)grub简介

  • 版本:
    grub 0.97: CentOS 6使用,grub经典版,以下介绍的都是此版本的操作
    grub 2:CentOS 7开始使用

  • grub的阶段:grub程序分布在硬盘的不同位置,被分为多个阶段

    • stage 1:bootloader,位于MBR,446字节
    • stage 1_5:位于MBR之后的扇区,确保stage1的bootloader能够识别stage2所在分区的文件系统
    • stage 2:
      1)显示启动菜单,实现与用户的交互
      2)加载用户选择的内核或操作系统
      3)为菜单提供了保护机制
  • stage 2 和内核文件一般安装至一个基本的磁盘分区(/boot一般是独立分区)

  • grub安装:grub-install命令
    将stage 1和stage 1_5安装至启动硬盘,并复制grub相关文件至启动硬盘的启动分区

(二)grub的详细配置

(1) grub命令行模式:
  • 命令:

    • help:获取帮助列表
    • help KEYWORD:详细帮助信息
    • root (hd#,#):指定grub的根分区,即stage2文件和内核文件所在的分区
    • kernel /PATH/TO/KERNEL_FILE:设定本次启动时用到的内核文件;额外还可添加许多内核支持使用的cmdline参数,可通过cat /proc/cmdline查询内核参数
    • initrd /PATH/TO/INITRAMFS_FILE:设定为选定的内核提供额外文件的ramdisk
    • boot:引导启动选定的内核
  • 硬盘分区编号规则(hd#,#):

    • hd#:磁盘编号,用数字表示;从0开始编号
    • #:分区编号,用数字表示; 从0开始编号
    • (hd0,0) 表示第一块硬盘,第一个分区
  • 手动通过grub命令行启动系统:

    grub> root (hd#,#)
    grub> kernel /vmlinuz-VERSION-RELEASE ro root=/dev/DEVICE
    grub> initrd /initramfs-VERSION-RELEASE.img
    grub> boot
    
(2)grub配置文件 /boot/grub/grub.conf:
  • 格式:
    通用设置
    default=#:默认启动菜单项,从0开始编号
    timeout=#:自动进入默认启动菜单项的倒计时
    splashimage=(hd#,#)/PATH/XPM_FILE:菜单界面背景图片
    hiddenmenu:默认隐藏启动菜单
    password [-md5|] string:启动菜单编辑认证
    以下是关于菜单项的设置,可以重复使用多次用于设置多个菜单项
    title TITLE:菜单项标题
    root (hd#,#):指定grub的根分区,即stage2文件和内核文件所在的分区
    kernel /PATH/TO/VMLINUZ_FILE:指定kernel文件的路径
    initrd /PATH/TO/INITRAMFS_FILE:指定内核匹配的initramfs文件路径
    password [-md5 | encrypted] string:启动选定内核或操作系统时认证

  • 生成grub密码:
    grub-md5-crypt:生成md5加密密码
    grub-crypt:生成sha512加密密码

  • 实验:/boot/grub/grub.conf文件格式要求

实验(1):判断kernel行和initrd行可否交换次序:
在/boot/grub/grub.conf文件中复制当前启动菜单选项设置,交换kernel行和initrd行的次序

重启系统,进入复制的启动项,发现错误提示:kernel必须在initrd前

按任意键回到启动菜单,键盘输入e后编辑启动项,综合使用键盘d:删除,键盘e:编辑,键盘o:新增加空行,将kernel行与initrd行交换次序,键盘b执行启动,最终成功启动

由于本次启动属于在grub中临时更改启动项,在系统启动之后,需要及时对/boot/grub/grub.conf文件进行修改,确保今后启动正常

实验(2):给grub的启动过程加密:

可以在grub的启动过程的两个环节加密:1)给启动菜单项编辑权限加密;2)启动特定菜单项的加密,加密命令相同,但是位于/boot/grub/grub.conf文件的不同位置

加密可以使用明文密码,但安全性太差。目前使用md5和sha-512加密方式加密,现在用md5加密启动项编辑权限,用sha-512加密启动特定菜单项

可以在vim编辑器下使用:r!grub-md5-cryptr!grub-crypt命令直接生成md5和sha-512加密密码。启动项编辑权限加密需要在选项信息前设置(第一个红框),启动特定菜单项需要在本启动菜单项后面设置(第二个红框)。

加密选项的语法为:
md5加密:password --md5 加密密码
sha-512加密:password --encrypted 加密密码

重启后发现启动界面没有e编辑选项,提示键入p输入密码,证明已被加密

选择加密过的启动项,回车后弹出密码输入提示,证明已被加密

四、启动过程错误修复

(一)仅保留/boot/grub目录下的grub.conf文件

mv /boot/grub/* /app
mv /app/grub.conf  /boot/grub

重启后发现系统正常启动,这说明进入stage 2必要的文件只有/boot/grub/grub.conf文件,其他文件只是stage1, stage 1_5, stage 2的备份文件

(二)grub的stage 1 丢失

mv /boot/grub/* /app
mv /app/grub.conf  /boot/grub                //在/boot/grub目录中只保留grub.conf文件
dd if=/dev/zero of=/dev/sda bs=1 count=446   //删除磁盘前446字节,即grub的stage 1

使用hexdump -C -n 512 /dev/sda查看/dev/sda磁盘的前512字节,发现前446字节已经清零

重启后,BIOS认为/dev/sda不是启动磁盘,当没有其他启动介质时,启动失败停在黑屏中。光盘启动,进入救援模式。在救援模式下,先进行切根操作,在使用grub-install /dev/sda命令修复,成功后即可正常启动。

修复成功后,可以看到在/boot/grub目录下丢失的文件也恢复了

(三)grub的stage 1_5丢失

dd if=/dev/zero of=/dev/sda bs=1 count=10240 skip=512 seek=512 //除stage1之外的前20个扇区的内容全部清零

使用hexdump -C -n 10240 /dev/sda查看/dev/sda磁盘的前10240字节,发现stage1_5确实已经清零。此时重启系统,BIOS发现磁盘是引导磁盘开始stage1,但是stage1_5被破坏,故启动失败。光盘启动,进入救援模式。

切根后,使用grub交互方式恢复stage1_5

grub> root (hd0,0)
grub> setup (hd0)

运行结果发现grub程序寻找/boot/grub下面的stage1_5文件尝试恢复并成功,故这种修复方式需要确保/boot/grub/下的stage文件没有缺损。修复成功后重启成功。

(四)/boot/grub/grub.conf文件丢失

当删除/boot/grub/grub.conf文件后,重新启动系统进入stage 2但是因为没有启动项设置,进入提示符。此时,手动输入关键启动参数root, kernel, initrd等关键信息,boot回车重启成功

(五)/boot/grub下的所有文件全部丢失

删除/boot/grub目录,此时stage2文件丢失,重新启动系统报错。进入救援模式,切根后使用grub-install /dev/sda命令修复恢复stage2文件。此外,手动编写grub.conf文件,提供启动菜单,最基本的grub.conf文件如下:

default=0
timeout=6
title linux_2.6
kernel /vmlinuz-2.6.32-696.el6.x86_64 root=/dev/sda2
initrd /initramfs-2.6.32-696.el6.x86_64.img

重启后,正常进入系统。

(六)/boot目录下的所有文件丢失

/boot目录下要恢复的文件如下:kernel文件,initramfs文件,grub stage2相关文件,grub.conf文件。这些文件的恢复操作上文都有所提及。

首先进入救援模式,切根(红框1)之后,挂载光盘(红框2),将光盘上的内核文件复制到/boot目录下(红框3),然后使用mkinitrd命令恢复initramfs文件(红框4)

使用grub-install命令恢复stage2 相关文件

编辑grub.conf文件

重新启动成功

(七)/sbin/init文件丢失

重启后,会因为没有/sbin/init文件而卡死在启动过程中。此时重新启动,编辑启动项,改为用bash启动。在bash环境下,用光盘重新安装init程序对应的软件包。

启动项画面键盘a键,进入启动项命令行编辑页面,指定由/bin/bash代替/sbin/init开始用户空间的初始化操作,行尾添加init=/bin/bash

重启后,发现可以在bash环境下操作。此时,用rpm命令查询/sbin/init文件对应的安装包为upstart,挂载光盘到/mnt目录为安装做准备。由于/目录挂载状态为只读,重新挂载并设置为读写。

rpm重新安装upstart软件包。因为只是丢失了/sbin/init文件,所以需要添加--force选项强制覆盖安装。

重新启动后,成功进入系统。

(八)/etc/fstab文件和/boot目录下的所有文件丢失

/boot目录下的文件丢失的情况,在上文第6条已经说明。但是,由于/etc/fstab文件丢失,在救援模式下执行/etc/rc.d/rc.sysinit初始化时无法挂载分区,救援系统无法自动将根分区挂载至/mnt/sysimage下,使后续对/boot目录文件丢失的修复操作无法进行。所以这种情况需要分两步进行:第一步,重建/etc/fstab文件。第二步,重启进入救援模式,再恢复/boot目录内容。

第一次进入救援模式,提示找不到硬盘分区。

此时,手动重建/etc/fstab文件。通过fdisk -l /dev/sda命令查看分区表,对每个分区表尝试挂载,了解分区内容,找到/boot分区和根分区,并在重建/etc/fstab文件中设置挂载信息。(红框1:确定未能成功挂载到/mnt/sysimage;红框2:尝试挂载/dev/sda1,挂载后分区没有内容,结合fdisk命令提示本分区属于启动分区,确定此分区应为/boot分区;红框3:尝试挂载/dev/sda2,挂在后分区内容很明显应为根分区。至于/dev/sda3,在fdisk命令结果已经显示为swap分区,不用再尝试挂载了)

此时重建fstab文件,需要注意由于之前根目录挂载失败,当前根分区挂载到/mnt/unknown目录下,则命令应为vi /mnt/unknown/etc/fstab。编辑时可以使用:r!blkid读入blkid的命令执行结果,加快编辑速度。保存退出,重启进入救援系统,此时系统正确识别文件系统。

此时的错误环境与上文第6条错误相同,修复方法参照上文。本次只尝试在安装kernel和initramfs文件的过程中,使用rpm命令安装光盘上的kernel的rpm包,提供另一种修复内核的方式。其他修复过程,完全相同。

此处需要注意的是:由于kernel的安装文件只破坏了一部分,rpm安装时必须添加--force选项强制覆盖,否则会提示已经安装而修复失败。

(九)逻辑卷环境下/etc/fstab文件和/boot目录下的所有文件丢失

与上文第8条错误相似,修复的方法也相似,但是需要考虑逻辑卷的特殊情况。重启进入救援模式,首先使用blkid, fdisk等命令查看磁盘分区情况,/boot目录不能被逻辑卷挂载,故/dev/sda1是boot分区,而/dev/sda2为逻辑卷。

lvscan命令发现当前的逻辑卷处于非活动状态,要想挂载逻辑卷,需要激活逻辑卷组,执行vgchange -ay激活所有逻辑卷,再用lvscan查看时发现已经处于活动状态。由于逻辑卷的名称非常直观,省去了挂载逻辑卷查看内容判断真实挂载分区的步骤。

此时应建立临时挂载目录如/mnt/temp,挂载操作mount /dev/VolGroup/lv_root /mnt/temp,随后手动建立fstab文件于/mnt/temp/etc/目录下。保存退出后,重启再次进入救援模式。

此后的修复方式与第8条错误的修复方案相似,都是rpm强制重装kernel的rpm包恢复/boot目录下的kernel和initramfs文件,接下来是grub-install命令修复/boot/grub下的驱动文件,最后是手动重建/boot/grub/grub.conf文件。(红框1:切换根目录,红框2:挂载光盘,红框3:rpm重装kernel包,红框4:grub-install修复)

在手动建立grub.conf文件时,注意根目录的名称此时为逻辑卷,其他没有变化。

保存重启,系统正常启动。

推荐阅读更多精彩内容