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以來統計人數
計數器
其他連結
環境工程模式篇
鳥園討論區
鳥哥舊站

今日 人數統計
昨日 人數統計
本月 人數統計
上月 人數統計