Arch Linux Boot Optimize
mirror from xdavidwu/arch-linux-boot-optimize
Arch Linux 開機加速Permalink
說明Permalink
雖然是在 Arch Linux 上實驗的,但想法都能套用在其他發行版上
要點Permalink
減少 kernel 和 initramfs 的大小、刪減不必要的操作,減肥減肥再減肥
測量Permalink
systemd-analyze
列出各個階段所花的時間
在我的環境下能列出 firmware
, loader
, kernel
, userspace
四項,大致可以理解為:
-
firmware
: UEFI 的部份 -
loader
: GRUB 的部份 -
kernel
: 從 kernel 開始 initialize 包含 initramfs 到 systemd 開始前的部份 -
userspace
: systemd 所花費的時間
systemd-analyze blame
可以列出 systemd 各項 service 花費的時間
systemd-analyze critical-chain
依相依性列出各項 service 啟動的時間點和花費的時間
sudo journalctl | grep Startup
這樣比較方便比較各次修改的結果
測試成果Permalink
kernel | vmlinuz size | human readable size |
---|---|---|
4.17.8-1-ARCH | 5326800 | 5.1M |
4.17.8-tekokomputilo | 3317808 | 3.2M |
kernel | initcpio type | initcpio libc | initcpio hooks | initramfs size | human readable size |
---|---|---|---|---|---|
4.17.8-1-ARCH | default | glibc | base udev autodetect modconf block filesystems keyboard fsck | 7831599 | 7.5M |
4.17.8-1-ARCH | fallback | glibc | base udev modconf block filesystems keyboard fsck | 28796670 | 28M |
4.17.8-tekokomputilo | default | musl | base-musl autodetect fsck-musl strip | 1288348 | 1.3M |
4.17.8-tekokomputilo | fallback | musl | base-musl fsck-musl strip | 3595931 | 3.5M |
kernel | systemd-analyze |
---|---|
4.17.8-1-ARCH | Startup finished in 3.920s (firmware) + 1.000s (loader) + 1.362s (kernel) + 771ms (userspace) = 7.054s |
4.17.8-tekokomputilo | Startup finished in 3.813s (firmware) + 661ms (loader) + 581ms (kernel) + 860ms (userspace) = 5.917s |
UEFIPermalink
這大概是最簡單的部分吧
把不必要的功能關掉,然後開機順序保持目標在第一順位
開機順序大多是影響最大的,尤其是網卡 PXE 的部分不需要就該移到後面
GRUBPermalink
loader
有包含從硬碟載入 kernel 和 initramfs 的時間,這部分影響最大,見下方 Linux kernel 和 initramfs 的部分
如果有多個 entry ,把目標設為預設,開機時壓著方向右或是 <enter>
就能消除人為的測量誤差
如果只有一個系統就直接把 GRUB_TIMEOUT
設為 0 吧
如果不在意外觀也可以不用圖形化的介面
再來影響小很多 (但可能還是測的出來) 的就是減少載入的 mod
在 UEFI 下預設會載入 GPT 和 MBR 兩種 table 的 mod ,如果不需要用 MBR 就在 /etc/default/grub
把他拿掉
顯示部分的 mod 在 UEFI 底下預設會載入 video.lst
列出的四種:
-
efi_uga
: 舊 EFI 時代的顯示方式 -
efi_gop
: UEFI 大多是這個 -
video_bochs
: bochs 模擬器 -
video_cirrus
: qemu 常用的 video
用 /etc/default/grub
的 GRUB_VIDEO_BACKEND
可以指定,如果有就只會載入指定的
修改 /etc/default/grub
後都需要手動 sudo grub-mkconfig -o /boot/grub/grub.cfg
才會套用,詳見 /etc/grub.d/00_header
font 觀察 /boot/grub/grub.cfg
,預設是載入 Arch Linux 的 /usr 裡面的,但可以發現另一個是直接 loadfont unicode
,直接搜尋 /boot/grub/font
裡面的,修改成這種方式可以跳過載入額外的分區
Linux kernelPermalink
自行針對自己的硬體編譯 Linux kernel ,可以減少 kernel image 的大小,進而減少 GRUB 載入 kernel 的時間,少一些部件需要 initialize 也會讓 kernel 啟動更快,甚至可以增進一點效能
在 config 時除了需不需要某項功能,還要考慮要將他 built-in 或者編譯成 module ,會需要的功能可以用官方 kernel 看自動載入了哪些 modules 再做刪減
built-in 的話在開機時就會 initialize ,會增加開機時間, module 時則是在 module 載入時才會運行
我的建議是將掛載 root 分區和基本的顯示及鍵盤會需要的功能都 built-in ,比如說 sata, ext4, efifb, atkbd 等,其餘有需要的再編成 module ,比如說顯卡、網卡、藍芽、網路功能、 iptables ,甚至 usb 等,交由 systemd 自動載入,以減少 kernel image 大小
一些安全性的保護措施可以根據實際應用需求做衡量,這部分大多數都是無法編譯成 modules 的
除此之外還要衡量 kernel 和 initramfs 的壓縮方式,有 xz, bzip2, gzip, lzo, lz4, lzma ,其中 xz 壓縮率最高但解壓慢, lzo 壓縮率最低但解壓極快,壓縮時的速度不需要考慮,各項可能需要分別試驗衡量一下
根據我的觀察 kernel 的解壓縮時間應該沒有被測量到,要注意
編譯器的 optimization level 如果是 -Os 或 -O2 可以在 config 內調整,其餘需要編輯 Makefile ,搜尋 -O2 去修改,常見有 -Os, -O2, -O3, -Ofast ,其中用 -Ofast 跑分會小幅高一點, -O2 是預設值, -Os 能在和 -O2 效能差不多的情況下把大小壓小, -O3 和 -Ofast 會顯著增加大小
Makefile 的 CFLAGS 可以針對 cpu 加上 -mtune=<cpu-type>
來指定可用的指令集範圍,如果是 generic 在大多數機子上都能跑, native 則是偵測當前機子上的,詳見 gcc 說明書
config 需要特別注意的是 CONFIG_HZ 的設定也可能會影響開機速度,建議保持預設的 1000
config 的方法常見的有 make menuconfig
和 make nconfig
,後者比較新
編譯時別忘了加 -j<n>
,其中 n 為最大同時 jobs 數,個人習慣實體核心數 *5 ,如果有 -j 後面不加數量就是不限制, gui 或 terminal 可能會暫時當掉
如果在別的地方編譯 make modules_install
可以用 INSTALL_MOD_PATH
變數指定複製到的路徑,方便把 modules 給分出來
如果編譯後還想再刪減,但不知道該從哪裡下刀可以用 ls -l */built-in.o
查看各個類別的大小
建議在桌機上編譯
我的 config ,針對 TOSHIBA Z930
initramfsPermalink
針對自己編譯的 kernel 製作 initramfs ,減少 initramfs 的大小,進而降低 bootloader 載入 initramfs 和 kernel 解壓縮 initramfs 的時間
initramfs 是透過 cpio 及 xz, bzip2, gzip, lzo, lz4, lzma 其中一個壓縮過後的 userspace ,可以自己打包,但求方便和功能完整還是用 Arch 特有的 mkinitcpio
和 lsinitcpio
處理
mkinitcpioPermalink
mkinitcpio -p <preset name>
是打包時用的指令
lsinitcpio <initramfs>
會列出 initramfs 裡的所有檔案,加個 -x
會解壓縮到當前路徑,可以用來觀察內容
可以由原本的 /etc/mkinitcpio.conf 和 /etc/mkinitcpio.d/linux.preset 複製一份出來修改,保留原本的 initramfs-linux.img 供出錯時使用
- FILES
如果 kernel 有 built-in 的功能需要 firmware ,在 initramfs 階段就需要提供,可以透過這一項加入
- HOOKS
在 conf 中可以設定需要的 hooks ,如果掛載 root 分區需要的模組都是 built-in 的,一般只會需要 base
就能開機,但建議還是加個 fsck
在掛載前自動檢查分區
hooks 的定義在 /lib/initcpio/install/ ( /libs/initcpio/hooks/ 是 initramfs 裡的模組,透過 hooks 加入 initramfs )
autodetect
會依據當前系統的需求調整其餘 hooks 安裝的檔案,例如如果在 fsck
前有 autodetect
,就只會加入機子上 root 分區檔案系統的對應 fsck 工具
strip
會 strip 目前已經加入的 library 和 binary ,刪除不必要的除錯用資訊以減少大小
所以,一般建議 hooks 為 (base autodetect fsck strip)
compiling binariesPermalink
用 lsinitcpio 或是觀察 hooks 的腳本可以發現,裡面含有的 binaries ,以上方的 hooks 設定為例通常會有 busybox
(來自 mkinitcpio-busybox 的版本), mount
, switch_root
, blkid
, fsck
, e2fsck (fsck.ext4, fsck.ext3, fsck.ext2)
, kmod ({dep,ins,rm,ls}mod mod{probe,info})
,都是由當前檔案系統複製出來的版本,使用了 glibc ,對於 initramfs 通常 musl 是更好的選擇,大小小很多,效能差異也不大, glibc 特有的功能在 initramfs 通常也都用不到,透過把 binaries 換成用 musl 自行編譯的版本會達到很棒的效果,在自行編譯時,也可以順便針對機子加入 -mtune=<cpu-type>
或是利用 -Os
,甚至自行斟酌 static link 或 dynamic link 來進一步減少 binaries 的大小
我採用的策略是使用 musl ,加上 -mtune 再加上 -Os , libc 採取 dynamic link 其餘採取 static link
musl 在 Arch 的 package 就叫做 musl
,裡面除了 musl libc 本身,還含有 musl-gcc
指令 (gcc 的 wrapper),方便使用 musl 編譯,通常在 configure 時把變數 CC 設為 musl-gcc ,或是編譯時到 Makefile 將 CC 設為 musl-gcc 即可
如果需要 musl 的 ldd ,例如在修改 mkinitcpio 找尋需要的 libraries 的部份,或是觀察 linking ,直接執行 /lib/ld-musl-*.so 即可,也可以把它 link 成 musl-ldd 之類的方便使用
kmod 的部份其實大部份只會用到 modprobe , fsck 也只會用到相對應的
busybox 的 config 方式是採用 Kconfig ,執行 make menuconfig
就能調整需要的功能
觀察 mkinitcpio 的 init 腳本就能找出所有需要的指令,可以減少到只剩需要的功能,也可以留下一些基本的指令用來除錯
busybox 的 mount
, switch_root
, modprobe
實做功能已經足夠在 initramfs 使用,可以改用 busybox 的實做減少實際需要的 binaries 數量
其中 busybox 的 mount 能判別出 ext 系列,但沒有分辨是 ext2, ext3 或 ext4 ,會從 ext2 開始試著 mount ,建議 cmdline 加入 rootfstype 直接指定
busybox 對於 blkid
就比較不全面了,只能查詢 UUID ,建議還是用一般的 blkid
其中比較需要注意的是 long options 的支援, musl 的 getopt 不會找尋 non-option 後方的 option ,但是 getopt_long 會,所以建議要打開,會用到的例子是 init 在 mount 的時候是執行 mount -t <type> <dev> <dist> -o <options>
,如果用 getopt 會抓不到後面的 -o 項,造成 busybox 檢查 args 的數量時出錯 (getopt 在這種情況下的表現其實 POSIX 沒有定義到)
我的 config
提供 blkid
, fsck
我的 config:
./configure --without-python --without-user --without-libz --without-cap-ng --without-tinfo --without-udev --without-util --without-ncursesw --without-ncurses --without-systemd --disable-shared CC="musl-gcc -no-pie" CFLAGS="-mtune=ivybridge -Os"
提供 e2fsck
,即 fsck.ext4
,fsck.ext3
, fsck.ext2
我的 config:
CC="musl-gcc -no-pie" CFLAGS="-mtune=ivybridge -Os" ./configure
Editing mkinitcpio scriptsPermalink
- HOOKS
可以透過修改 install hooks 的名稱方便讓原汁原味的 initcpio 和修改版並存 例如 base-musl, fsck-musl
- functions
/lib/initcpio/functions
包含 mkinitcpio scripts 常用的 functions, 其中要注意的是 add_binary
內含利用 ldd
找出需要的 libraries 的部份, 需要加入 musl-ldd
處理 musl 的部份
systemdPermalink
servicesPermalink
首先當然是停用不必要的 service ,以 sudo systemctl disable
停用,列出已經啟用的 service 最簡單的方式就是利用 bash-completion 在 disable 後方按兩次 <tab>
鍵
再來觀察 systemd-analyze blame
,不必要且沒有明確 enable 但自動載入的可以用 sudo systemctl mask
擋掉
journal flushPermalink
如果系統用久了就會發現 systemd-journal-flush.service
占很大的時間,但如果停用它 journal 就會留在 /run 而不是移到 /var ,關機了就沒了,解決方法是停用他但在其他時機手動 flush ,或是限制 journal 的大小避免花費太多時間,見 /etc/systemd/journald.conf
remountPermalink
如果把 fsck 交由 initramfs 實行, systemd remount 一次 root 就顯得多餘了,確定 cmdline 有 rootflags=rw 等想要的 mount flags 後可以把 /etc/fstab 的 root 註解掉避免 remount
mandbPermalink
現在 Arch 的作法是每隔 12h 開機時更新一次 mandb ,但這通常很慢,一個作法是改回原本的方法,利用 pacman hooks 在有更動到 manpages 時更新,不過這把時間轉嫁給了 pacman ,在 AUR 上有 mandb-ondemand ,同樣是個 pacman hook 但利用 systemd 在背景執行不須等待,安裝它後 mask man-db.service 即可
quiet bootPermalink
解決大部分的東西後, message 也顯得不太重要了, vga console 或 framebuffer console 的效能都不佳,甚至可能成為瓶頸,建議停用一些 message 來加速開機
kernel console printkPermalink
直接在 config 中把 printk 的功能拿掉或許會減少很多空間,但這樣就很難 debug 了,建議留著,讓他只把錯誤訊息輸出在 console 就好
確定 kernel 的 cmdline 有 quiet
這項,可以透過 /proc/cmdline
檢查,從 /etc/default/grub
的 GRUB_CMDLINE_LINUX
修改
systemd boot messagePermalink
如果 cmdline 有了 quiet , systemd 就只會在出錯時開始輸出,但有時候還是會有不需要又關不掉的功能的報錯,比如說找不到 autofs4 的 module 之類的,這時候還是可以在 cmdline 加入 systemd.show_status=false
強制關掉
關了這些後,理想狀況下 linux 的開機輸出就差不多只剩 initramfs 的 fsck 了,就把他留著吧
i915Permalink
如果使用 Intel 內顯, kernel module i915
有個 parameter 叫做 fastboot , modinfo 如下:
parm: fastboot:Try to skip unnecessary mode sets at boot time (default: false) (bool)
實測打開他並不太影響 systemd-analyze
測出來的數值,但會解決開機時螢幕暗約一秒的問題,比較早看到 login prompt ,達到實際使用上的加速
Linux kernel 5.1 上的 i915 已改為 1 / 0 組合, 預設為 -1 (per chip default)
parm: fastboot:Try to skip unnecessary mode sets at boot time (0=disabled, 1=enabled) Default: -1 (use per-chip default) (int)
改之前觀察 /sys/module/i915/parameters/fastboot
可以看出用的是哪一種,或者先試一種看有沒有噴錯也是可行
Comments on Matrix #comments_xdavidwu.link_/archlinux/arch-boot-opt/:xdavidwu.link