內核/傳統編譯

出自 Arch Linux 中文维基

本文介紹從 kernel.org 的源代碼編譯自己的內核。這是一種對所有發行版均適用的傳統方法。取決於你的背景,這也可能比用 ABS 工具編譯內核更複雜。請考慮使用 Arch 構建系統以使重複的編譯任務更加有效和安全。

準備[編輯 | 編輯原始碼]

準備編譯時,不需要也不建議使用 root 賬號或用 sudo 等工具獲取 root 權限。

安裝核心軟件包[編輯 | 編輯原始碼]

安裝 base-devel包組 軟件組,這個組包含了 makegcc 等我們需要的軟件包。參考 Arch 默認內核的 PKGBUILD,同時建議安裝以下軟件包:xmltokmodinetutils, bclibelfgitcpioperltarxz

創建內核編譯目錄[編輯 | 編輯原始碼]

建議創建單獨的內核編譯目錄,本文使用 home 下的 kernelbuild 目錄。

$ mkdir ~/kernelbuild

獲取源碼[編輯 | 編輯原始碼]

警告: systemd 需要至少 3.12 版本的內核(cgroups 支持需要 4.2 及以上的版本)。參考 /usr/share/doc/systemd/README

https://www.kernel.org 下載源碼,得到你所選的內核版本對應的 tarball (tar.xz) 文件。

可以在瀏覽器中簡單地右鍵 tar.xz 鏈接並選擇 保存鏈接為 下載;也可以通過 HTTP、TFTPRsync 或者 Git 的各種圖形化或命令行工具下載。

注意:
  • 驗證下載到的任何內核 tarball 文件的 PGP 簽名總是好的做法。這樣可以確保它是合法的,並有助於建立信任網絡。參見 kernel.org/signature
  • 對於某些版本(例如 -rc 版本),下載的存檔和生成的目錄不會嚴格遵循本頁示例中使用的 A.B.C 命名,請根據你的需要進行調整。

下面例子中使用 wget 下載 A.B.C 內核到 ~/kernelbuild 目錄:

$ cd ~/kernelbuild
$ wget https://cdn.kernel.org/pub/linux/kernel/vA.x/linux-A.B.C.tar.xz

在信任下載的壓縮包之前,你還得驗證下載的正確性。 首先抓取簽名,然後用它來抓取簽名密鑰的指紋,然後使用指紋來獲取實際的簽名密鑰:

$ wget https://cdn.kernel.org/pub/linux/kernel/vA.x/linux-A.B.C.tar.sign
$ gpg --list-packets linux-A.B.C.tar.sign
$ gpg --recv-keys fingerprint-from-previous-step

注意簽名是為 tar 存檔(即擴展名 .tar)生成的,而不是為下載的壓縮 .tar.xz 文件生成的。 你需要解壓後者而不解壓它。 檢查你是否已安裝 xz,然後這樣繼續:

$ unxz linux-A.B.C.tar.xz
$ gpg --verify linux-A.B.C.tar.sign linux-A.B.C.tar

如果沒看到"Good signature"就別繼續。

如果在構建目錄中沒使用wget,就需要手動將壓縮包移入其中,例如:

$ mv /path/to/linux-A.B.C.tar.xz ~/kernelbuild/

半官方內核鏡像[編輯 | 編輯原始碼]

一些 kernel.org Git 存儲庫的半官方鏡像由其各自的維護者提供。這些通常比 kernel.org 的版本更新。

  • Linus Torvalds 放在 GitHub 上的主線內核

https://github.com/torvalds/linux.git.

  • Greg Kroah-Hartman 放在 GitHub 上的穩定分支

https://github.com/gregkh/linux. [1]

解壓內核代碼[編輯 | 編輯原始碼]

  • 將其解壓後進入源碼目錄:
$ tar -xvf linux-A.B.C.tar

為了絕對確保不發生權限錯誤,需要運行 chown 將文件夾的所有權轉移給當前用戶。

要將包含其中每個文件的文件夾的所有權轉移,運行 chown 命令。

$ chown -R $USER:$USER linux-A.B.C 

文件夾中每個文件的所有權轉移給你了,所以不會遇到任何與權限相關的錯誤。

為了完成準備工作,內核樹需要絕對乾淨;不要覺得解壓後源代碼樹是乾淨的。所以,切換到創建的新內核源目錄,然後運行 make mrproper 命令:

$ cd linux-A.B.C
$ make mrproper
注意: make mrproper 的目標依賴於 make clean 的目標,所以沒必要同時執行兩者。參考[2]

配置[編輯 | 編輯原始碼]

這是自定義內核以適應計算機的精確規格的最關鍵步驟。內核配置在 .config 文件中設置,其中包括 Kernel modules 的使用情況。正確設置 .config 中的選項,你的計算機會獲得最高的運行效率。

注意: 現階段無需使用 root 帳戶或 root 權限。

參考使用下面內容可以節省時間:

  • 使用 Arch 官方內核的設置(推薦)
  • 生成正在運行內核的配置文件和設置(手動,適用於高級用戶但不推薦)

導出當前內核設置[編輯 | 編輯原始碼]

這個命令將使用默認的 Arch 內核設置為自定義內核創建一個 .config 文件。如果正在運行官方 Arch 內核,你可以在內核源碼目錄中使用以下命令:

$ zcat /proc/config.gz > .config

也可以在 official Arch Linux kernel 中找到默認配置。

提示:
  • 如果你在升級內核,一些選項可能改了或被刪掉了。在這種情況下,在 #Compilation 下運行 make 時,它會要求你提供版本之間更改的每個配置選項的答案。要使用默認值而不用一直按Enter,請運行 make olddefconfig
  • modprobed-db 可用於從默認 Arch .config 中刪除不需要的模塊。獲得 properly populated database 後,運行 make LSMOD=$HOME/.config/modprobed.db localmodconfigmodprobed.db 中刪除所有不存在的模塊。
警告: 如果你用當前的 .config 文件編譯內核,別忘了在新的 .config 中的重命名內核版本,就是"CONFIG_LOCALVERSION"這項。或者用#高級配置中列出的用戶界面之一中的"General Setup > Local version - append to kernel release"更改。如果跳過這一步,就有把當前內核覆蓋的風險。

高級配置[編輯 | 編輯原始碼]

提示:如果不想在啟動和關閉時看到很多內核輸出,可以禁用相關的調試選項。

有多種方式可以協助配置內核:

注意: 下面列出的工具提供每個內核功能的三個配置選項:y 表示啟用,n 表示禁用,m 表示啟用作為內核模塊(必要時加載)。
  • make menuconfig: 老的 ncurses 界面,被 nconfig 取代
  • make nconfig: 新的命令行 ncurses 界面
  • make xconfig: 用戶友好的圖形界面,需要安裝 packagekit-qt5。建議沒有經驗的用戶使用此方法。
  • make gconfig: 和 xconfig 類似的圖形界面,使用 gtk.

在內核源代碼目錄執行選擇的方法,將生成一個全新的.config,可選參數默認是選中的,但是如果有老內核的 .config 文件,這些參數不一定會被選中。

改變配置文件並保存。建議在源代碼目錄外備份.config,因為你可能需要編譯多次,直到所有的選項都配置正確。如果不確定每個選項的影響,每次編譯只改變少量選項。如果無法啟動新構建的內核,參見這裡查看必須的配置項列表。從liveCD運行$ lspci -k #以列出使用中的內核模塊的名字。最重要的是,你必須保留 systemd 需要的 CGROUPS 支持。

從live media運行 lspci -k # 會列出正在使用內核模塊的名稱。重中之重,你必須保留cgroup支持,這是systemd的依賴。參閱 Gentoo:Kernel/Gentoo Kernel Configuration GuideGentoo:Intel#KernelGentoo:Ryzen#Kernel(適用於 Intel 或 AMD Ryzen CPU)。

編譯[編輯 | 編輯原始碼]

如果希望 gcc 根據處理器指令集進行優化, 編輯內核目錄下的 arch/x86/Makefile (i686) 或 arch/x86_64/Makefile (86_64)文件:

  • 查找 Processor type and features > Processor Family 中設置的處理器系列 CONFIG_MK8,CONFIG_MPSC,CONFIG_MCORE2,CONFIG_MATOM,CONFIG_GENERIC_CPU
  • 將 cc 調用選項設置為 -march=native,例如 cflags-$(CONFIG_MK8) += $(call cc-option,-march=native).

對 32bit 內核,需要編輯 arch/x86/Makefile_32.cpu 並設置處理器的 -march=native

編譯時間將從15分鐘到超過一小時不等。這很大程度依賴於選擇了多少選項/模塊和處理器性能。詳情參考 Makeflags.config 配置好之後,在內核目錄運行:

$ make
提示:為了更快地編譯,可以使用 -jX 參數運行 make 以並行編譯,其中 X 是整數。最快的編譯通常是通過使用機器中 CPU 核心的數量來實現的; 例如,對於雙核處理器,運行 make -j2。 參閱 Makepkg#Improving compile times

安裝[編輯 | 編輯原始碼]

安裝模塊[編輯 | 編輯原始碼]

內核編譯了,它的模塊也隨之而來。首先構建模塊:

$ make modules
警告: 從這裡開始,需要 root 權限執行命令,否則會失敗.

編譯完內核後編譯模塊:

# make modules_install

這會將編譯後的模塊複製到 /lib/modules/A.B.C/ 中。 這使得單獨使用的各個內核的模塊保持分離。

提示:如果系統需要正常 Linux 內核外的模塊,需要在定製內核安裝完成之後重新編譯它們。Nvidia 顯卡模塊可以參考 NVIDIA#Custom kernel

拷貝內核到 /boot 目錄[編輯 | 編輯原始碼]

注意: 確保已從你的系統架構的目錄複製 bzImage 內核文件。見下文。

內核編譯過程將生成該內核的壓縮 bzImage,如果沒有,運行:

make bzImage

必須將這個 bzImage 複製到 /boot 目錄並重命名。只要名稱帶有 vmlinuz- 前綴,你就可以根據需要命名內核。 在下面的示例中,已安裝並編譯的 A.B.C 內核已被複製並重命名為 vmlinuz-linuxAB

# cp -v arch/x86/boot/bzImage /boot/vmlinuz-linuxAB

製作初始化內存盤[編輯 | 編輯原始碼]

初始化內存盤,是一個在真正的根文件系統可訪問之前被掛載的初始化根目錄文件系統。初始化內存盤(initrd)被綁定到對應的內核,且作為內核啟動過程的一部分被載入。內核將該初始化內存盤(initrd)作為兩階段啟動過程的一部分掛載起來,以載入模塊使得真正的文件系統可用,然後得到真正的根文件系統。初始化內存盤包含實現以上過程的目錄和可執行文件的最小集合,例如用來將內核模塊安裝到內核的insmod工具。對桌面或服務器Linux系統來說,初始化內存盤(initrd)就是一個臨時的文件系統。其生命周期短暫,僅作為到真正的根文件系統的橋梁。在沒有可變存儲的嵌入式系統中,初始化內存盤就是永久的根文件系統。參考 Initramfs on Wikipediamkinitcpio

/boot 中的文件可以取任何名字。然而,為了方便區分,建議採取統一的命名規範,例如使用 linux<major revision><minor revision>

如果你使用LILO且其無法與內核設備映射器驅動連通,你可能需要先運行modprobe dm-mod命令。

自動生成[編輯 | 編輯原始碼]

可以複製和修改現有的 mkinitcpio preset,以便可以按照與官方內核相同的方式生成自定義內核 initramfs image。 這在重新編譯內核(例如更新時)時會節省很多時間。 在下面的示例中,將為上面安裝的內核A.B.C複製並修改庫存 Arch 內核的預設文件。


首先,複製現有的預設文件,將其重命名為與複製 bzImage 時指定為 /boot/vmlinuz- 後綴的自定義內核的名稱相匹配:

# cp /etc/mkinitcpio.d/linux.preset /etc/mkinitcpio.d/linuxAB.preset

其次,編輯文件並針對自定義內核進行修改。注意 ALL_kver= 參數也與複製 bzImage 時指定的自定義內核的名稱匹配:

/etc/mkinitcpio.d/linuxAB.preset
...
ALL_kver="/boot/vmlinuz-linuxAB"
...
default_image="/boot/initramfs-linuxAB.img"
...
fallback_image="/boot/initramfs-linuxAB-fallback.img"

最後,生成 initramfs 映像:

# mkinitcpio -p linuxAB

手動方法[編輯 | 編輯原始碼]

不使用 preset 文件,可以用 mkinitcpio 手動生成:

# mkinitcpio -k <kernelversion> -g /boot/initramfs-<file name>.img
  • -k (--kernel <kernelversion>): 生成 initramfs image 的內核版本. <kernelversion> 需要和內核源代碼目錄和/usr/lib/modules/下的模塊目錄一致.
  • -g (--generate <filename>): 要生成的 initramfs 文件。

例如,上面安裝的A.B.C自定義內核的命令為:

# mkinitcpio -k A.B.C -g /boot/initramfs-linuxAB.img

拷貝 System.map[編輯 | 編輯原始碼]

System.map 文件並非 Linux 啟動所必須的。它包含一張該次構建的內核的標識符之列表(例如函數名,變量名等等)和它們相應的地址。該「標識符到地址的映射」是用於:

  • 某些進程,像 klogd, ksymoops 等
  • OOPS 處理程序,當內核崩潰出錯信息釋放到屏幕時(例如類似於內核在某個函數中崩潰了的信息)。
提示:UEFI 分區使用 FAT32 文件系統,不支持軟鏈接。

如果 /boot 支持軟鏈接(即不是 FAT32),將 System.map 複製到 /boot, 然後創建 /boot/System.map 軟鏈接到 /boot/System.map-YourKernelName:

# cp System.map /boot/System.map-YourKernelName
# ln -sf /boot/System.map-YourKernelName /boot/System.map

如果 /boot 是不支持軟鏈接的 FAT32,則需要將第二條命令換成 cp 來複製文件。

完成以上所有步驟之後,你的/boot目錄中應該多出以下三個文件和一個軟鏈接:

  • Kernel: vmlinuz-YourKernelName
  • Initramfs: Initramfs-YourKernelName.img
  • System Map: System.map-YourKernelName
  • System.map 軟鏈接(或者復件)

Bootloader 設置[編輯 | 編輯原始碼]

在你的啟動器(bootloader)配置文件中為你神奇的新內核添加一個入口。範例見GRUB, LILO, GRUB2 or Syslinux

提示:內核源碼中有一個為LILO準備的腳本,以自動化實現該步驟:$ arch/x86/boot/install.sh。記得以root權限執行lilo來更新它。