Linux 基础学习训练教材 - CentOS 7.x

第 13 堂课:服务管理与开机流程管理

Linux 的服务管理,就是玩一玩 systemctl 这个指令!还有开机流程的管理,也就是 grub2 啰!

最近更新时间: 2017/04/05

之前的课程介绍过 process 与 program 的差别,也谈过 PID 信息的观察,以及包括 job control 等与进程相关的数据。 本节课会继续介绍 process 管理所需要具备的 signal 信息。另外,管理员是需要管理服务的,每个服务都是需要被启动的 process。 最终会介绍开机流程到底是如何运作。

13.1:服务管理

服务就是一个被启动的进程,这个进程可以常驻于内存当中提供网络连接、例行工作调度等任务,就可称为服务。

13.1.1:进程的管理通过 kill 与 signal

一个程序被运行触发之后会变成在内存当中的一个活动的单位,那就是进程 (process)。之前的课程介绍过 PID 与进程的观察, 本小节会继续介绍 PID 的管理方面的任务。

管理员可以通过给某进程一个信号 (signal) 去告知该进程你想要让它作什么。主要的进程信号可以使用 kill -l 或 man 7 signal 查找, 底下截取较常见的信号代号与对应内容:

代号名称内容
1SIGHUP启动被终止的进程,可让该 PID 重新读取自己的设置档,类似重新启动
2SIGINT相当于用键盘输入 [ctrl]-c 来中断一个进程的进行
9SIGKILL代表强制中断一个进程的进行,如果该进程进行到一半, 那么尚未完成的部分可能会有『半产品』产生,类似 vim会有 .filename.swp 保留下来。
15SIGTERM以正常的结束进程来终止该进程。由于是正常的终止, 所以后续的动作会将他完成。不过,如果该进程已经发生问题,就是无法使用正常的方法终止时, 输入这个 signal 也是没有用的。
19SIGSTOP相当于用键盘输入 [ctrl]-z 来暂停一个进程的进行

至于传输 signal 则是通过 kill 这个指令。举例来说,若管理员想要直接让前一堂课介绍的 rsyslogd 这个进程重读其设置档, 而不通过服务管理的正常机制时,可以尝试如下处理方式:

[root@localhost ~]# pstree -p | grep rsyslog
           |-rsyslogd(6701)-+-{rsyslogd}(6708)
           |                |-{rsyslogd}(6709)
           |                `-{rsyslogd}(6710)

[root@localhost ~]# kill -1 6701
[root@localhost ~]# tail /var/log/messages
.......
May 24 14:57:37 www rsyslogd: [origin software="rsyslogd" swVersion="7.4.7" 
  x-pid="6701" x-info="http://www.rsyslog.com"] rsyslogd was HUPed

读者可以发现在注册表出现了 rsyslogd 被要求重新读取设置档的记录 (HUPed)!而除了 PID 之外,管理员也能够使用指令名称来给予 signal, 直接通过 killall 即可。如下管理方式:

[root@localhost ~]# killall -1 rsyslogd
例题:
  1. 使用 ps 这个指令,列出系统全部进程的『 pid, nice值, pri值, command 』信息
  2. 找出系统内进程运行文件名为 sshd 的 PID
  3. 将上述的 PID 给予 signal 1 的方式为何?
  4. 观察一下 /var/log/secure 的内容是否正确的输出相关的进程行为?
  5. 如何将系统上所有的 bash 进程通通删除?

13.1.2:systemd 简介

从 CentOS 7.x 以后,Red Hat 系列的 distribution 放弃沿用多年的 System V 开机启动服务的流程, 改用 systemd 这个启动服务管理机制~采用 systemd 的原因如下:

  • 平行处理所有服务,加速开机流程
  • 一经要求就回应的 on-demand 启动方式 (因为 systemd 为单一进程且常驻于内存)
  • 服务相依性的自我检查
  • 依 daemon 功能分类
  • 将多个 daemons 集合成为一个群组

但是 systemd 也有许多存在的问题:

  • 全部的 systemd 都用 systemctl 这个管理程序管理,而 systemctl 支持的语法有限制,不可自订参数。
  • 如果某个服务启动是管理员自己手动运行启动,而不是使用 systemctl 去启动的,那么 systemd 将无法侦测到该服务
  • systemd 启动过程中,无法与管理员通过 standard input 传入消息!因此,自行撰写 systemd 的启动设置时,务必要取消交互机制
  • systemd 的设置档放置目录

基本上, systemd 将过去所谓的 daemon 运行脚本通通称为一个服务单位 (unit),而每种服务单位依据功能来区分时,就分类为不同的类型 (type)。 基本的类型有包括系统服务、数据监听与交换的插槽档服务 (socket)、保存系统状态的快照类型、提供不同类似运行等级分类的操作环境 (target) 等等。 至于设置档都放置在底下的目录中:

  • /usr/lib/systemd/system/:每个服务最主要的启动脚本设置;
  • /run/systemd/system/:系统运行过程中所产生的服务脚本,这些脚本的优先序要比 /usr/lib/systemd/system/ 高!
  • /etc/systemd/system/:管理员依据主机系统的需求所创建的运行脚本,运行优先序又比 /run/systemd/system/ 高!

也就是说,到底系统开机会不会运行某些服务其实是看 /etc/systemd/system/ 底下的设置,所以该目录底下就是一大堆链接档。而实际运行的 systemd 启动脚本设置档, 其实都是放置在 /usr/lib/systemd/system/ 底下的!

  • systemd 的 unit 类型分类说明

/usr/lib/systemd/system/ 内的数据主要使用扩展名来进行分类,底下尝试找出 cron 与 multi-user 这些服务的数据:

[root@localhost ~]# ll /usr/lib/systemd/system/ | grep -E '(multi|cron)'
-rw-r--r--. 1 root root  284  7月 27  2015 crond.service
-rw-r--r--. 1 root root  597 11月 20  2015 multipathd.service
-rw-r--r--. 1 root root  492 11月 20  2015 multi-user.target
drwxr-xr-x. 2 root root 4096  2月 18 02:56 multi-user.target.wants
lrwxrwxrwx. 1 root root   17  2月 18 02:55 runlevel2.target -> multi-user.target
lrwxrwxrwx. 1 root root   17  2月 18 02:55 runlevel3.target -> multi-user.target
lrwxrwxrwx. 1 root root   17  2月 18 02:55 runlevel4.target -> multi-user.target

所以我们可以知道 crond 其实算是系统服务 (service),而 multi-user 要算是运行环境相关的类型 (target type)。根据这些扩展名的类型, 我们大概可以找到几种比较常见的 systemd 的服务类型如下:

扩展名主要服务功能
.service 一般服务类型 (service unit):主要是系统服务,包括服务器本身所需要的本机服务以及网络服务都是!比较经常被使用到的服务大多是这种类型!
.socket

内部进程数据交换的插槽服务 (socket unit): 这种类型的服务通常在监控消息传递的插槽档,当有通过此插槽档传递消息来说要链接服务时,就依据当时的状态将该用户的要求发送到对应的 daemon, 若 daemon 尚未启动,则启动该 daemon 后再发送用户的要求。

使用 socket 类型的服务一般是比较不会被用到的服务,因此在开机时通常会稍微延迟启动的时间。一般用于本机服务比较多, 例如我们的图形界面很多的软件都是通过 socket 来进行本机进程数据交换的行为。

.target 运行环境类型 (target unit):其实是一群 unit 的集合,例如上面表格中谈到的 multi-user.target 其实就是一堆服务的集合~也就是说, 选择运行 multi-user.target 就是运行一堆其他 .service 或/及 .socket 之类的服务就是了!

其中又以 .service 的系统服务类型最常见。

例题:
  1. 通过 ps 找出 systemd 这个运行档的完整路径
  2. 上述的指令是由那一个软件所提供?
  3. 该软件提供的全部文件名如何查找?

13.1.3:systemctl 管理服务的启动与关闭

一般来说,服务的启动有两个阶段,一个是『开机的时候设置要不要启动这个服务』, 以及『现在要不要启动这个服务』两个阶段。 这两个阶段都可以使用 systemctl 指令来管理。systemctl 的基本语法为:

[root@localhost ~]# systemctl [command] [unit]

上表所谓的 command 主要有:

  • start :立刻启动后面接的 unit
  • stop :立刻关闭后面接的 unit
  • restart :立刻关闭后启动后面接的 unit,亦即运行 stop 再 start 的意思
  • reload :不关闭后面接的 unit 的情况下,重新加载设置档,让设置生效
  • enable :设置下次开机时,后面接的 unit 会被启动
  • disable :设置下次开机时,后面接的 unit 不会被启动
  • status :目前后面接的这个 unit 的状态,会列出有没有正在运行、开机缺省运行否、登录等信息等!
例题:
  1. 查找系统有没有 chronyd 这个指令?
  2. 使用 rpm 查找该指令属于哪个软件?
  3. 使用 rpm 查找该软件的功能为何?
  4. 请观察 chronyd 这个服务目前是启动或关闭?开机时会不会启动这个服务?
  5. 请将 chronyd 关闭,且下次开机还是会关闭
  6. 再次观察 chronyd 这个服务。
  7. 观察注册表有没有记录 chronyd 这个服务的相关数据?

13.1.4:systemctl 列表系统服务

缺省的情况下, systemctl 可以列出目前系统已经启动的服务群,如下列表:

[root@localhost ~]# systemctl
UNIT                            LOAD   ACTIVE SUB       DESCRIPTION
.......
chronyd.service                 loaded active running   NTP client/server
crond.service                   loaded active running   Command Scheduler
swap.target                     loaded active active    Swap
sysinit.target                  loaded active active    System Initialization
timers.target                   loaded active active    Timers
systemd-tmpfiles-clean.timer    loaded active waiting   Daily Cleanup of Temporary Directories

LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.

153 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.

列表当中,LOAD/ACTIVE/DESCRIPTION 等意义为:

  • UNIT :项目的名称,包括各个 unit 的类别 (看扩展名)
  • LOAD :开机时是否会被加载,缺省 systemctl 显示的是有加载的项目而已喔!
  • ACTIVE :目前的状态,须与后续的 SUB 搭配!就是我们用 systemctl status 观察时,active 的项目!
  • DESCRIPTION :服务的详细描述

如上表显示 chronyd 为 service 的类别,下次开机会启动 (load),而现在的状态是运作中 (active running)。最底下两行显示共有 153 的 unit 显示在上面, 如果想要列出系统上还没有被列出的服务群,可以加上 --all 来继续观察。此外,我们也能够仅针对 service 的类别来观察,如下所示:

[root@localhost ~]# systemctl list-units --type=service --all

如果想要观察更详细的每个启动的数据,可以通过底下的方式来处理:

[root@localhost ~]# systemctl list-unit-files
UNIT FILE                                   STATE
proc-sys-fs-binfmt_misc.automount           static
dev-hugepages.mount                         static
.......
mdadm-last-resort@.timer                    static
systemd-readahead-done.timer                static
systemd-tmpfiles-clean.timer                static
unbound-anchor.timer                        disabled

368 unit files listed.
例题:
  1. 找出系统中以 ksm 为开头的所有的服务名称,并观察其状态
  2. 将该服务设置为『开机不启动』且『目前立刻关闭』的情况

13.1.5:systemctl 取得与切换缺省操作界面

Linux 缺省的操作画面可以是纯文本也能够是文本加上图形界面。早期的 systemV 系统称文本界面为 runlevel 3 而图形界面为 runlevel 5。 systemd 提供多种的操作界面,主要是通过『 target 』这种 unit 来作为规范。读者可以使用如下的指令来观察所有的 target:

[root@localhost ~]# systemctl list-units --type=target --all

在 CentOS 7 底下常见的操作界面 (target unit) 有底下几种:

  • multi-user.target:纯文本模式
  • graphical.target:文本加上图形界面,其实就是 multi-user.target 再加图形操作。
  • rescue.target:在无法使用 root 登录的情况下,systemd 在开机时会多加一个额外的暂时系统,与你原本的系统无关。这时你可以取得 root 的权限来维护你的系统。
  • emergency.target:紧急处理系统的错误,还是需要使用 root 登录的情况,在无法使用 rescue.target 时,可以尝试使用这种模式!
  • shutdown.target:就是关机的流程。
  • getty.target:可以设置你需要几个 tty 之类的,如果想要降低 tty 的项目,可以修改这个东西的设置档!

而上述的操作模式中,缺省的是 multi-user 与 graphical 这两种。其实这些模式彼此之间还是有相依性的,读者可以使用如下的方式查出来 graphical 运行前, 有哪些 target 需要被运行:

[root@localhost ~]# systemctl list-dependencies graphical.target
graphical.target
● ├─.......
● └─multi-user.target
●   ├─.......
●   ├─basic.target
●   │ ├─.......
●   │ ├─sockets.target
●   │ │ └─.......
●   │ ├─sysinit.target
●   │ │ ├─.......
●   │ │ ├─local-fs.target
●   │ │ │ └─.......
●   │ │ └─swap.target
●   │ │   └─.......
●   │ └─timers.target
●   │   └─.......
●   ├─getty.target
●   │ └─.......
●   ├─nfs-client.target
●   │ └─.......
●   └─remote-fs.target
●     └─nfs-client.target
●       └─.......

上述的表格已经精简化过,仅保留了 unit=target 的项目,从里面读者也能够发现到要运行 graphical 之前,还得需要其他的 target 才行。 若须取得目前的操作界面,可以使用如下的方式来处理:

[root@localhost ~]# systemctl get-default
graphical.target

若需要设置缺省的操作界面,例如将原本的图形界面改为文本界面的操作方式时,可以使用如下的方式来处理:

[root@localhost ~]# systemctl set-default multi-user.target
Removed symlink /etc/systemd/system/default.target.
Created symlink from /etc/systemd/system/default.target to
        /usr/lib/systemd/system/multi-user.target.

[root@localhost ~]# systemctl get-default
multi-user.target

如此即可将文本界面设置为缺省的操作环境。上述的作法是开机时才进行的缺省操作环境界面,若需要即时将图形界面改为文本界面, 或者反过来处理时,可以使用如下的方式来处置:

[root@localhost ~]# systemctl isolate multi-user.target
例题:
  1. 使用 netstat -tlunp 查看一下系统的网络监听端口口
  2. 请在本机目前的状态下,将操作界面模式更改为 rescue.target 这个救援模式
  3. 使用 netstat -tlunp 查看一下系统的网络监听端口口是否有变少?
  4. 将环境改为原本的操作界面

13.1.6:网络服务管理初探

如果是网络服务,一般都会启动监听界面在 TCP 或 UDP 的封包端口口上。取得目前监听的端口口可以使用如下的方式:

[root@localhost ~]# netstat -tlunp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address      Foreign Address  State    PID/Program name
tcp        0      0 192.168.122.1:53   0.0.0.0:*        LISTEN   1452/dnsmasq
tcp        0      0 0.0.0.0:22         0.0.0.0:*        LISTEN   29941/sshd
tcp        0      0 127.0.0.1:631      0.0.0.0:*        LISTEN   29938/cupsd
tcp        0      0 127.0.0.1:25       0.0.0.0:*        LISTEN   30092/master
tcp6       0      0 :::22              :::*             LISTEN   29941/sshd
tcp6       0      0 ::1:631            :::*             LISTEN   29938/cupsd
tcp6       0      0 ::1:25             :::*             LISTEN   30092/master
udp        0      0 0.0.0.0:53273      0.0.0.0:*                 29287/avahi-daemon:
udp        0      0 192.168.122.1:53   0.0.0.0:*                 1452/dnsmasq
udp        0      0 0.0.0.0:67         0.0.0.0:*                 1452/dnsmasq
udp        0      0 0.0.0.0:5353       0.0.0.0:*                 29287/avahi-daemon:
udp        0      0 0.0.0.0:514        0.0.0.0:*                 29256/rsyslogd
udp6       0      0 :::514             :::*                      29256/rsyslogd

重点在 Local Address 那一行,会显示该服务是启动在本机的哪一个 IP 界面的哪一个端口口上,如此管理员即可了解启动该端口口的服务是哪一个。 若无须该网络服务,则可以将该进程关闭。以上述表格来说,如果需要关闭 avahi-daemon 以及 cupsd 时,可以使用如下的方式取得服务名称:

[root@localhost ~]# systemctl list-unit-files | grep -E '(avahi|cups)'
cups.path                    enabled
avahi-daemon.service         enabled
cups-browsed.service         disabled
cups.service                 enabled
avahi-daemon.socket          enabled
cups.socket                  enabled

若需要将其关闭,则应该使用如下的方式,将『目前』与『缺省』的服务启动都关闭才行:

[root@localhost ~]# systemctl stop avahi-daemon.service avahi-daemon.socket
[root@localhost ~]# systemctl stop cups.path cups.service cups.socket
[root@localhost ~]# systemctl disable avahi-daemon.service avahi-daemon.socket
[root@localhost ~]# systemctl disable cups.path cups.service cups.socket
[root@localhost ~]# netstat -tlunp

读者将可发现到 avahi-daemon 以及 cupsd 的服务已经被关闭。而若需要启动某个网络服务,则需要了解到该服务是由哪一个软件所启动的, 该软件需要先安装后才可以启动该服务。

例题:
  1. WWW 网络服务是由 httpd 这个软件所提供的,请先安装该软件
  2. 查找是否有 httpd 的服务存在了?
  3. 启动该服务,同时设置为缺省启动该服务
  4. 查找端口口是否顺利启动 port 80
  5. 使用浏览器查找本机 WWW 服务是否正确启动了。
  6. 将 port 80 的防火墙放行。

13.2:开机流程管理

系统如果出错,可能需要进入救援模式才能够处理相关的任务。但如何进入救援模式?这就需要从开机流程分析来下手。

13.2.1:Linux 系统在 systemd 底下的开机流程

一般正常的情况下, Linux 的开机流程会是如下所示:

  1. 加载 BIOS 的硬件信息与进行自我测试,并依据设置取得第一个可开机的设备;
  2. 读取并运行第一个开机设备内 MBR 的 boot Loader (亦即是 grub2, spfdisk 等程序);
  3. 依据 boot loader 的设置加载 Kernel ,Kernel 会开始侦测硬件与加载驱动程序;
    • 加载 kernel file 与 initramfs 文件在内存内解压缩
    • initramfs 会在内存仿真出系统根目录,提供 kernel 相关的驱动程序模块
    • 内核设备驱动程序完整的驱动硬件
  4. 在硬件驱动成功后,Kernel 会主动调用 systemd 程序,并以 default.target 流程开机;
    • systemd 运行 sysinit.target 初始化系统及 basic.target 准备操作系统;
    • systemd 启动 multi-user.target 下的本机与服务器服务;
    • systemd 运行 multi-user.target 下的 /etc/rc.d/rc.local 文件;
    • systemd 运行 multi-user.target 下的 getty.target 及登录服务;
    • systemd 运行 graphical 需要的服务

如上,读者们可以发现内核文件驱动系统完成后,接下来就是 systemd 的任务,也就是前一小节所探讨的内容。但内核文件在哪里? 以及如何设置不同的内核文件开机,那就是开机管理程序的任务了。

例题:
  1. 使用 systemctl list-units --all 的功能,找出 local 关键字
  2. 使用 systemctl list-unit-files 的功能,找出 local 关键字
  3. 使用 systemctl show xxx.service 的功能,找出上述软件的运行档
  4. 查阅 /etc/rc.d/rc.local 的权限,同时加上 x 的权限
  5. 重新加载 systemd ,让上述修订生效
  6. 使用 systemctl list-units --all 的功能,找出 local 关键字,是否为 active 呢?

13.2.2:内核与内核模块

系统的内核大多放置于 /boot/vmlinuz* 开头的文件中,而 initramfs 则放置于 /boot/initramfs* 。 至于内核的模块则放置于 /lib/modules/$(uname -r)/ 目录内。

目前系统上面已经加载的模块,可以使用底下的方式来观察:

[root@localhost ~]# lsmod
[root@localhost ~]# lsmod | grep xfs

找到名为 xfs 的模块后,若想了解该模块的功能,可以使用如下的方式查找:

[root@localhost ~]# modinfo xfs
filename:       /lib/modules/3.10.0-327.el7.x86_64/kernel/fs/xfs/xfs.ko
license:        GPL
description:    SGI XFS with ACLs, security attributes, no debug enabled
author:         Silicon Graphics, Inc.
alias:          fs-xfs
rhelversion:    7.2
srcversion:     978077FBDF054363971A9EE
depends:        libcrc32c
intree:         Y
vermagic:       3.10.0-327.el7.x86_64 SMP mod_unload modversions
signer:         CentOS Linux kernel signing key
sig_key:        79:AD:88:6A:11:3C:A0:22:35:26:33:6C:0F:82:5B:8A:94:29:6A:B3
sig_hashalgo:   sha256

若想要加载某个模块,就使用 modprobe 来加载,卸载则使用 modprobe -r 来卸载即可。

例题:
  1. 在内核模块的目录下,使用 find 找出系统有没有 fat 关键字的模块?
  2. 是否已经有加载 fat 相关的模块了?若无,请加载该模块,再次检查是否加载成功。
  3. 再次检查有无 cifs 模块,若无,请加载,并查找该模块的功能为何?
  4. 卸载 cifs 模块。
  5. 在内核模块的目录下,有没有 ntfs 的关键字?
  6. 在 yum 的使用上,激活 epel 软件库,搜索 ntfs 这个关键字软件
  7. 尝试安装上述找到的软件名称
  • 使用 /etc/sysctl.conf 处理内核参数

某些情况下,你会需要更动内核参数。而缺省的内核参数字于 /proc/sys/ 底下。一般不建议用户直接使用手动修改方式处理 /proc 内的文件 (因为下次开机就不会持续提供),应使用修改 /etc/sysctl.conf 来处理。举例而言,若你的 server 不想要回应 ping 的封包, 则可以如此测试:

[root@localhost ~]# ping -c 2 localhost
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.050 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.049 ms

--- localhost ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.049/0.049/0.050/0.007 ms

[root@localhost ~]# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
[root@localhost ~]# ping -c 2 localhost
PING localhost (127.0.0.1) 56(84) bytes of data.
--- localhost ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 999ms

[root@localhost ~]# echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all

读者可以发现 icmp 确实不会回应 ping 的要求了。而这个设置值如果一定要每次开机都生效, 可以写入 sysctl.conf 内,写法为:

[root@localhost ~]# vim /etc/sysctl.conf
net.ipv4.icmp_echo_ignore_all = 1

[root@localhost ~]# sysctl -p
[root@localhost ~]# cat /proc/sys/net/ipv4/icmp_echo_ignore_all
1

如此则可以每次都生效了。不过,这个功能对于内部环境的测试还是很重要的,因此还是请修订回来比较妥当。

例题:
  1. 请将 icmp_echo_ignore_all 改为缺省的不要启动 (0)
  2. 让系统缺省启动 IP 转递 (IP forward) 的功能。

13.2.3:grub2 设置档初探

内核的加载与设置是由开机管理程序来处理的,而 CentOS 7 缺省的开机管理程序为 grub2 这一个软件。该软件的优点包括有:

  • 认识与支持较多的文件系统,并且可以使用 grub2 的主程序直接在文件系统中搜索内核文件名;
  • 开机的时候,可以『自行编辑与修改开机设置项目』,类似 bash 的指令模式;
  • 可以动态搜索设置档,而不需要在修改设置档后重新安装 grub2 。亦即是我们只要修改完 /boot/grub2/grub.cfg 里头的设置后,下次开机就生效了!
  • 磁盘在 grub2 内的代号定义

开机时,数据得从磁盘读出,因此磁盘、分区的代号信息得先要了解厘清才行。 grub2 对磁盘的代号定义如下:

(hd0,1)         # 一般的缺省语法,由 grub2 自动判断分割格式
(hd0,msdos1)    # 此磁盘的分割为传统的 MBR 模式
(hd0,gpt1)      # 此磁盘的分割为 GPT 模式
  • 硬盘代号以小括号 ( ) 包起来;
  • 硬盘以 hd 表示,后面会接一组数字;
  • 以『搜索顺序』做为硬盘的编号!(这个重要!)
  • 第一个搜索到的硬盘为 0 号,第二个为 1 号,以此类推;
  • 每颗硬盘的第一个 partition 代号为 1 ,依序类推。

所以说,整个硬盘代号为:

硬盘搜索顺序在 Grub2 当中的代号
第一颗(MBR)(hd0) (hd0,msdos1) (hd0,msdos2) (hd0,msdos3)....
第二颗(GPT)(hd1) (hd1,gpt1) (hd1,gpt2) (hd1,gpt3)....
第三颗(hd2) (hd2,1) (hd2,2) (hd2,3)....
  • /boot/grub2/grub.cfg 设置档的理解

基本上,开机时 grub2 会去读取的设置档就是 grub.cfg 这个文件,但是这个文件是由系统程序分析创建的,不建议读者们手动修改。 因此底下读者先观察该文件内容即可,先不要修订。

[root@localhost ~]# cat /boot/grub2/grub.cfg
### BEGIN /etc/grub.d/00_header ###
set pager=1

if [ -s $prefix/grubenv ]; then
  load_env
fi
.......
if [ x$feature_timeout_style = xy ] ; then
  set timeout_style=menu
  set timeout=5
# Fallback normal timeout code in case the timeout_style feature is
# unavailable.
else
  set timeout=5
fi
### END /etc/grub.d/00_header ###

### BEGIN /etc/grub.d/00_tuned ###
set tuned_params=""
### END /etc/grub.d/00_tuned ###

### BEGIN /etc/grub.d/01_users ###
if [ -f ${prefix}/user.cfg ]; then
  source ${prefix}/user.cfg
  if [ -n ${GRUB2_PASSWORD} ]; then
    set superusers="root"
    export superusers
    password_pbkdf2 root ${GRUB2_PASSWORD}
  fi
fi
### END /etc/grub.d/01_users ###

### BEGIN /etc/grub.d/10_linux ###
menuentry 'CentOS Linux (3.10.0-327.el7.x86_64) 7 (Core)' --class centos --class gnu-linux 
  --class gnu --class os --unrestricted $menuentry_id_option 
  'gnulinux-3.10.0-327.el7.x86_64-advanced-fb871e94-6242-48c9-82ee-3c2df02a070e' {
        load_video
        set gfxpayload=keep
        insmod gzio
        insmod part_gpt
        insmod xfs
        set root='hd0,gpt2'
        if [ x$feature_platform_search_hint = xy ]; then
          search --no-floppy --fs-uuid --set=root --hint='hd0,gpt2'  .....
        else
          search --no-floppy --fs-uuid --set=root a026bf1c-3028-4962-88e3-cd92c6a2a877
        fi
        linux16 /vmlinuz-3.10.0-327.el7.x86_64 root=/dev/mapper/centos-root ro 
                rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet LANG=en_US.UTF-8
        initrd16 /initramfs-3.10.0-327.el7.x86_64.img
}
.......
### END /etc/grub.d/10_linux ###
.......

### BEGIN /etc/grub.d/40_custom ###
# This file provides an easy way to add custom menu entries.  Simply type the
# menu entries you want to add after this comment.  Be careful not to change
# the 'exec tail' line above.
### END /etc/grub.d/40_custom ###

上表中 menuentry 后面接的就是菜单的的标题与实际的内容了。而该内容比较重要的项目有:

  • set root='hd0,gpt2':这 root 是指定 grub2 设置档所在的那个设备。如果你下达 df 这个指令,会发现到 /boot 这个目录是挂载于 /dev/vda2 这个设备上, 因此设置信息就是在 /dev/vda2 亦即 grub2 的 (hd0,2) 。也因为我们用的是 gpt 的分割格式,因此系统就用 (hd0,gpt2) 来显示。
  • linux16 /vmlinuz-3.10.0-327.el7.x86_64 root=/dev/mapper/centos-root ....:这一行指的是内核文件在哪里?因为我们的内核在 /boot/vmlinuz.... 上面, 而 /boot 是在 /dev/vda2 上面,因此文件名为 (/dev/vda2)/vmlinuz-...,由于上一个 set root 已经指定了 (hd0,gpt2) 了,因此这里就简写为 /vmlinuz-... 啰。 后面参数接的 root 就是 Linux 根目录所在了。
  • initrd16 /initramfs-3.10.0-327.el7.x86_64.img:指的当然就是 initramfs 文件的所在,文件名的设计与 linux16 相同。

13.2.4:grub2 设置档维护

基本上,修改 grub2 设置档你可以在如下的位置进行:

  • /etc/default/grub:主要修改环境设置
  • /etc/grub.d/ :可以设置其他菜单

主要环境设置内容为:

[root@www ~]# cat /etc/default/grub
GRUB_TIMEOUT=5                   # 指定缺省倒数读秒的秒数
GRUB_DEFAULT=saved               # 指定缺省由哪一个菜单来开机,缺省开机菜单之意
GRUB_DISABLE_SUBMENU=true        # 是否要隐藏次菜单,通常是藏起来的好!
GRUB_TERMINAL_OUTPUT="console"   # 指定数据输出的终端机格式,缺省是通过文本终端机
GRUB_CMDLINE_LINUX="rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet"
                                 # 就是在 menuentry 括号内的 linux16 项目后续的内核参数
GRUB_DISABLE_RECOVERY="true"     # 取消救援菜单的制作

若有修改上述文件,则需要使用 grub2-mkconfig -o /boot/grub2/grub.cfg 来进行修订。现在假设:

  • 开机菜单等待 40 秒钟
  • 缺省用第一个菜单开机
  • 菜单请显示出来不要隐藏
  • 内核外带『elevator=deadline』的参数值

那应该要如何处理 grub.cfg 呢?基本上,你应该要修订 /etc/default/grub 的内容如下:

[root@localhost ~]# vim /etc/default/grub
GRUB_TIMEOUT=40
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=menu
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet elevator=deadline"
GRUB_DISABLE_RECOVERY="true"

修改完毕之后再来则是进行输出修订的任务:

[root@localhost ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-327.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-327.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-741c73b552ed495d92a024bc7a9768cc
Found initrd image: /boot/initramfs-0-rescue-741c73b552ed495d92a024bc7a9768cc.img
done

若想要知道是否完整的变更了,请 vim /boot/grub2/grub.cfg 查阅相关设置值是否变更即可。

  • 菜单建置的脚本 /etc/grub.d/*

grub2-mkconfig 运行之后会去分析 /etc/grub.d/* 里面的文件,然后运行该文件来建置 grub.cfg。至于 /etc/grub.d/ 目录底下会有这些文件存在:

  • 00_header:主要在创建初始的显示项目,包括需要加载的模块分析、屏幕终端机的格式、倒数秒数、菜单是否需要隐藏等等,大部分在 /etc/default/grub 里面所设置的变量,大概都会在这个脚本当中被利用来重建 grub.cfg 。
  • 10_linux:根据分析 /boot 底下的文件,尝试找到正确的 linux 内核与读取这个内核需要的文件系统模块与参数等,都在这个脚本运作后找到并设置到 grub.cfg 当中。 因为这个脚本会将所有在 /boot 底下的每一个内核文件都对应到一个菜单,因此内核文件数量越多,你的开机菜单项目就越多了。 如果未来你不想要旧的内核出现在菜单上,那可以通过移除旧内核来处理即可。
  • 30_os-prober:这个脚本缺省会到系统上找其他的 partition 里面可能含有的操作系统,然后将该操作系统做成菜单来处理就是了。 如果你不想要让其他的操作系统被侦测到并拿来开机,那可以在 /etc/default/grub 里面加上『 GRUB_DISABLE_OS_PROBER=true 』取消这个文件的运作。
  • 40_custom:如果你还有其他想要自己手动加上去的菜单项目,或者是其他的需求,那么建议在这里补充即可!

所以,一般来说,我们会更动到的就是仅有 40_custom 这个文件即可。那这个文件内容也大多在放置管理员自己想要加进来的菜单项目就是了。 好了,那问题来了,我们知道 menuentry 就是一个菜单,那后续的项目有哪些东西呢?简单的说,就是这个 menuentry 有几种常见的设置? 亦即是 menuentry 的功能啦!常见的有这几样:

  • 直接指定内核开机:

    基本上如果是 Linux 的内核要直接被用来开机,那么你应该要通过 grub2-mkconfig 去抓 10_linux 这个脚本直接制作即可,因此这个部份你不太需要记忆! 因为在 grub.cfg 当中就已经是系统能够捉到的正确的内核开机菜单了!不过如果你有比较特别的参数需要进行呢?这时候你可以这样作: (1)先到 grub.cfg 当中取得你要制作的那个内核的菜单项目,然后将它拷贝到 40_custom 当中 (2)再到 40_custom 当中依据你的需求修改即可。

    这么说或许你很纳闷,我们来做个实际练习好了:

    问:
    如果你想要使用第一个原有的 menuentry 取出来后,增加一个菜单,该菜单可以强制 systemd 使用 graphical.target 来启动 Linux 系统, 让该菜单一定可以使用图形界面而不用理会 default.target 的链接,该如何设计?
    答:
    当内核外带参数中,有个『 systemd.unit=??? 』的外带参数可以指定特定的 target 开机!因此我们先到 grub.cfg 当中,去拷贝第一个 menuentry , 然后进行如下的设置:
    [root@study ~]# vim /etc/grub.d/40_custom
    menuentry 'My graphical CentOS, with Linux 3.10.0-229.el7.x86_64' --class rhel fedora
              --class gnu-linux --class gnu --class os --unrestricted --id 'mygraphical' {
            load_video
            set gfxpayload=keep
            insmod gzio
            insmod part_gpt
            insmod xfs
            set root='hd0,gpt2'
            if [ x$feature_platform_search_hint = xy ]; then
              search --no-floppy --fs-uuid --set=root --hint='hd0,gpt2'  94ac5f77-cb8a-495e-a65b-...
            else
              search --no-floppy --fs-uuid --set=root 94ac5f77-cb8a-495e-a65b-2ef7442b837c
            fi
            linux16 /vmlinuz-3.10.0-229.el7.x86_64 root=/dev/mapper/centos-root ro rd.lvm.lv=
                    centos/root rd.lvm.lv=centos/swap crashkernel=auto rhgb quiet 
                    elevator=deadline systemd.unit=graphical.target
            initrd16 /initramfs-3.10.0-229.el7.x86_64.img
    }
    # 请注意,上面的数据都是从 grub.cfg 里面拷贝过来的,增加的项目仅有特殊字体的部份而已!
    # 同时考量画面宽度,该项目稍微被变动过,请依据您的环境来设置喔!
    
    [root@study ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
    
    当你再次 reboot 时,系统就会多出一个菜单给你选择了!而且选择该菜单之后,你的系统就可以直接进入图形界面 (如果有安装相关的 X window 软件时), 而不必考量 default.target 是啥东西了!了解乎?
  • 通过 chainloader 的方式移交 loader 控制权

    所谓的 chain loader (开机管理程序的链结) 仅是在将控制权交给下一个 boot loader 而已, 所以 grub2 并不需要认识与找出 kernel 的文件名 ,『 他只是将 boot 的控制权交给下一个 boot sector 或 MBR 内的 boot loader 而已 』 所以通常他也不需要去查验下一个 boot loader 的文件系统!

    一般来说, chain loader 的设置只要两个就够了,一个是预计要前往的 boot sector 所在的分区代号, 另一个则是设置 chainloader 在那个分区的 boot sector (第一个磁区) 上!假设我的 Windows 分区在 /dev/sda1 ,且我又只有一颗硬盘,那么要 grub 将控制权交给 windows 的 loader 只要这样就够了:

    menuentry "Windows" {
            insmod chain      # 你得要先加载 chainloader 的模块对吧?
            insmod ntfs       # 建议加入 windows 所在的文件系统模块较佳!
            set root=(hd0,1)  # 是在哪一个分区~最重要的项目!
            chainloader +1    # 请去 boot sector 将 loader 软件读出来的意思!
    }
    

    通过这个项目我们就可以让 grub2 交出控制权了!

    问:
    假设你的测试系统上面使用 MBR 分区,并且出现如下的数据:
    [root@study ~]# fdisk -l /dev/vda
       Device Boot      Start         End      Blocks   Id  System
    /dev/vda1            2048    10487807     5242880   83  Linux
    /dev/vda2   *    10487808   178259967    83886080    7  HPFS/NTFS/exFAT
    /dev/vda3       178259968   241174527    31457280   83  Linux
    
    其中 /dev/vda2 使用是 windows 7 的操作系统。现在我需要增加两个开机选项,一个是取得 windows 7 的开机菜单,一个是回到 MBR 的缺省环境,应该如何处理呢?
    答:
    windows 7 在 /dev/vda2 亦即是 hd0,msdos2 这个地方,而 MBR 则是 hd0 即可,不需要加上分区啊!因此整个设置会变这样:
    [root@study ~]# vim /etc/grub.d/40_custom
    menuentry 'Go to Windows 7' --id 'win7' {
            insmod chain
            insmod ntfs
            set root=(hd0,msdos2)
            chainloader +1
    }
    menuentry 'Go to MBR' --id 'mbr' {
            insmod chain
            set root=(hd0)
            chainloader +1
    }
    
    [root@study ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
    
    另外,如果每次都想要让 windows 变成缺省的开机选项,那么在 /etc/default/grub 当中设置好『 GRUB_DEFAULT=win7 』 然后再次 grub2-mkconfig 这样即可啦!不要去算 menuentry 的顺序喔!通过 --id 内容来处理即可!

13.2.5:开机文件的救援问题

一般来说,如果是文件系统错误,或者是某些开机过程中的问题,我们可以通过开机时进入 grub2 的交互界面中, 在 linux16 的字段,加入 rd.break 或者是 init=/bin/bash 等方式来处理即可。但是,如果是 grub2 本身就有问题, 或者是根本就是内核错误,或者是 initramfs 出错时,那就无法通过上述的方式来处理了。

在 CentOS 7 的操作经验中,在升级内核时,偶而会有 initramfs 制作错误的情况导致新内核无法开机的问题。 此时,若你已经没有保留旧的内核,此时就无法顺利开机了。

要处理这个问题,最常见的就是通过『原版光盘开机,然后使用救援模式 (rescue) 来自动侦测硬盘系统, 再通过 chroot 的动作,同时使用 dracut 来重建 initramfs 』即可。

  1. 调整 BIOS 变成光盘开机 (或 USB 开机),同时放入原版光盘,之后开机
  2. 进入光盘安装模式后,选择『 Troubleshooting 』的项目,再选择『 Rescue a CentOS Linux system 』环境
    • 此时系统会自动侦测硬盘,然后加载适当的模块,之后应该会找到我们的硬盘
    • 当出现 1)Continue, 2)Read-only mount, 3)Skip to shell, 4)Quit(reboot) 时,按下 1 即可!
    • 若一切都顺利,光盘环境会提供『 chroot /mnt/sysimage 』指令,作为切换成原本系统的手动。
  3. 进入 shell 环境后,输入『 df 』应该会看到原本系统数据通通挂载在 /mnt/sysimage 底下, 因此请使用『 chroot /mnt/sysimage 』指令来进入原本的系统。
  4. 使用底下的指令来找到 initramfs 的文件名:
    sh4.2# grep init /boot/grub2/grub.cfg
            initrd16 /initramfs-3.10.0-514.el7.x86_64.img
            initrd16 /initramfs-0-rescue-741c73b552ed495d92a024bc7a9768cc.img
    
    上面那个 initramfs-3.10.0-514.el7.x86_64.img 就是等等我们需要创建的文件名了!
  5. 通过 dracut 来进行 initramfs 的重建,重建的方法也很简单!最重要的是取得内核的版本。 从上面的查找来看,我们的内核版本应该是 3.10.0-514.el7.x86_64,所以建置的方式为:
    sh4.2# dracut -v /boot/initramfs-3.10.0-514.el7.x86_64.img 3.10.0-514.el7.x86_64
    sh4.2# touch /.autorelabel
    sh4.2# exit   
    sh4.2# reboot
    
    当然,你也可以选择其他的内核,来开机,不过我们这里就使用缺省内核即可。这样应该就可以救援你的系统了! 这个光盘救援的步骤最好能够多操作几次,偶而它会是你的救命符!

13.3:课后练习操作

前置动作:请使用 unit13 的硬盘进入作业环境,并请先以 root 身分运行 vbird_book_setup_ip 指令设置好你的学号与 IP 之后,再开始底下的作业练习。

请使用 root 的身份进行如下实做的任务。直接在系统上面操作,操作成功即可,上传结果的程序会主动找到你的实做结果。

  1. 系统救援:
    • 你目前这个系统上,由于某些缘故, initramfs 文件已经失效,所以应该是无法顺利开机成功。
    • 请进入系统救援的模式,并依据系统既有的内核版本,将 initramfs 重建
    • 注意,重建时,应考虑 grub2 的原本设置档,以找到正确的文件名,方可顺利成功开机喔
    • 不要忘记了,如果顺利开机成功,请记得运行 vbird_book_setup_ip 设置好学号与 IP
  2. 请回答下列问题,并将答案写在 /root/ans13.txt 文件内:
    1. 管理系统的 process 时,通常是使用给予信号 (signal) 的方式。而手动给予 Signal 的指令常见有哪两个?
    2. 承上,常见的 signal 有 1, 9, 15, 19,各代表甚么意思?
    3. 在 CentOS 7 系统上,所有的 systemd 服务脚本 (无论有没有 enable) 放在哪个目录内?
    4. 承上,但是系统【缺省开机会加载】的脚本,又是放在哪个目录内?
    5. systemd 会将服务进行分类,主要分为 X.service, X.socket, X.target,请问这几个类型分别代表甚么意思?
    6. 在此课堂上,启动系统缺省的网络服务,可以使用那五个口诀来处理?每个口诀对应的指令为何?
  3. Systemd 的操作与内核功能
    1. 通过网络服务监听端口口观察的指令查出系统有多少服务在启动?无论如何,请将服务关闭到只剩下 port 22 与 port 25 两个。 在底下有其他服务启动后,自然会有多的端口口,不过在这个题目前,只能有这两个端口口的存在。
    2. 让这部 Linux 主机,缺省会启动在纯文本模式下,亦即开机时,缺省不会有图形接口
    3. 让系统缺省启动 IP 转递 (IP forward) 的功能。
    4. 系统开机之后,会自动寄出一封 email 给 root,说明系统开机了。指令可以是『 echo "reboot new" | mail -s 'reboot message' root 』, 请注意,这个动作必须是系统『自动于开机完成后就动作』,而不需要用户或管理员登录喔!
  4. 缺省服务的启动
    1. 请依据课堂上的服务启动口诀,启动 WWW 服务。并假设你知道 WWW 的首页目录位于 /var/www/html/ 以及首页文件名为 index.html。 请在 index.html 内以 vim 新建两行,分别是学号与姓名。
    2. 请依据课堂上的服务启动口诀,启动 FTP 服务。并假设你知道 FTP 的首页目录在 /var/ftp ,你要让 /etc/fstab 提供给用户端以 ftp://your.server.ip/pub/fstab 的网址下载,该如何拷贝 /etc/fstab 到正确的位置去?
  5. grub2 相关应用
    1. 修改开机时的默认值,让菜单等待达到 30 秒
    2. 让开机时,内核加入 noapic 及 noacpi 两个缺省参数
    3. 增加一个菜单,菜单名称为【 Go go MBR 】,通过 chainloader 的方式,让这个菜单出现在开机时的选择画面中 (但是,默认值还是正常的 Linux 开机菜单)
    4. 以缺省的正常 Linux 开机菜单为依据范本,再创建一个名为【Graphical Linux】的菜单,这个菜单会强制进入图形接口, 而不是缺省的文本接口。(hint: systemd.unit=???)

作业结果传输:请以 root 的身分运行 vbird_book_check_unit 指令上传作业结果。 正常运行完毕的结果应会出现【XXXXXX;aa:bb:cc:dd:ee:ff;unitNN】字样。若需要查阅自己上传数据的时间, 请在操作系统上面使用: http://192.168.251.250 检查相对应的课程文件。

修改历史:
  • 2016/05/26:完成!
  • 2017/04/04:加入了 sysctl.conf 的使用以及加入了习题啰!
  • 2017/04/05:没有想到第一题习题就出问题,忘记没有讲解原版ISO开机的救援问题...所以加上 13.2.5 的开机救援方式!
2016/05/26以来统计人数
计数器
其他链接
环境工程模式篇
鸟园讨论区
鸟哥旧站

今日 人数统计
昨日 人数统计
本月 人数统计
上月 人数统计