---
title: 章 8. 設定 FreeBSD 核心
part: 部 II. 一般作業
prev: books/handbook/multimedia
next: books/handbook/printing
showBookMenu: true
weight: 11
params:
  path: "/books/handbook/kernelconfig/"
---

[[kernelconfig]]
= 設定 FreeBSD 核心
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:sectnumoffset: 8
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/handbook/kernelconfig/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

[[kernelconfig-synopsis]]
== 概述

核心 (Kernel) 是 FreeBSD 作業系統最重要的部份之一。它負責記憶體管理、安全控管、網路、硬碟存取等等。 儘管目前 FreeBSD 大多可以用動態設定， 但有時仍需要設定並編譯自訂的核心。

讀完這章，您將了解：

* 何時需要編譯自訂核心。
* 如何取得硬體資訊。
* 如何量身訂做核心設定檔。
* 如何使用核心設定檔來建立並編譯新的核心。
* 如何安裝新的核心。
* 發生錯誤時如何排除問題。

所有在本章所列出的指令均應以 `root` 來執行。

[[kernelconfig-custom-kernel]]
== 為何要編譯自訂的核心?

早期的 FreeBSD 的核心 (Kernel) 被戲稱為 "`巨石`"。因為當時的核心是一個非常大的程式，且只支援固定的硬體裝置，如果您想改變核心的設定，就必須編譯一個新核心並重新開機，才能使用。

現今，大多數在 FreeBSD 核心的功能已採用模組 (Module) 的方式包裝，並可依需求動態從核心載入或卸載。 這使得執行中的核心能夠快速適應新硬體環境並在核心開啟新的功能，這就是所謂模組化核心 (Modular Kernel)。

儘管如此，還是有一些功能因使用到靜態的核心設定須要編譯，因為這些功能與核心緊密結合，無法將做成可動態載入的模組。且部份強調安全性的環境會盡量避免載入與卸載核心模組，且只要將需要的功能靜態的編譯到核心當中。

編譯自訂的核心幾乎是每位進階的 BSD 使用者所必須經歷的過程。儘管這項工作可能比較耗時，但在 FreeBSD 的使用上會有許多好處。 跟必須支援大多數各式硬體的 [.filename]#GENERIC# 核心相比的話， 自訂的核心可以更『體貼』，只支援『自己硬體』的部分就好。 自訂核心有許多項優點，如：

* 加速開機，因為自訂的核心只需要偵測您系統上存在的硬體，所以讓啟動所花的過程更流暢快速。
* 減少記憶體使用，自訂的核心通常會比 [.filename]#GENERIC# 核心使用更少的記憶體，這很重要，因為核心必須一直存放在實體記憶體內，會讓其他應用程式無法使用。因此，自訂核心對於記憶體較小的系統來說，發揮很大的作用。
* 支援額外的硬體，自訂的核心可以增加一些 [.filename]#GENERIC# 核心沒有提供的硬體支援。

在編譯自訂核心之前，請思考要這麼做的原因，若是因為需要特定硬體的支援，很可能已有既有的模組可以使用。

核心模組會放在 [.filename]#/boot/kernel# 並且可使用 man:kldload[8] 動態載入到執行中的核心。大部份的核心驅動程式都有可載入的模組與操作手冊。例如 man:ath[4] 無線乙太網路驅動程式在其操作手冊有以下資訊：

[source,shell]
....
Alternatively, to load the driver as a module at boot time, place the
following line in loader.conf(5):

    if_ath_load="YES"
....

加入 `if_ath_load="YES"` 到 [.filename]#/boot/loader.conf# 會於開機期間自動載入這個模組。

部份情況在 [.filename]#/boot/kernel# 會沒有相關的模組，這對於某些子系統大多是真的。

[[kernelconfig-devices]]
== 偵測系統硬體

在編輯核心設定檔之前，建議先調查清楚機器各項硬體資訊。在雙作業系統的環境，也可透過其他作業系統來了解目前機器上的硬體資訊。 舉例來說，Microsoft(TM) 的 裝置管理員 (Device Manager) 內會有目前已安裝的硬體資訊。

[NOTE]
====
某些版本的 Microsoft(TM) Windows(TM) 會有系統 (System) 圖示可用來進入 裝置管理員。
====

若 FreeBSD 是唯一安裝的作業系統，則可使用 man:dmesg[8] 來查看開時時系統偵測到的硬體資訊 。FreeBSD 上大多硬體驅動程式都有操作手冊會列出支援的硬體。例如，以下幾行是說 man:psm[4] 驅動程式偵測到了一隻滑鼠：

[source,shell]
....
psm0: <PS/2 Mouse> irq 12 on atkbdc0
psm0: [GIANT-LOCKED]
psm0: [ITHREAD]
psm0: model Generic PS/2 mouse, device ID 0
....

因為該硬體存在，此驅動程式便不應該從自訂核心設定檔中移除。

若 `dmesg` 輸出的結果未顯示開機偵測硬體的部份，則可改閱讀 [.filename]#/var/run/dmesg.boot# 檔案的內容。

另外，也可以透過 man:pciconf[8] 工具可用來查詢硬體資訊，該工具會列出更詳細的硬體資訊如：

[source,shell]
....
% pciconf -lv
ath0@pci0:3:0:0:        class=0x020000 card=0x058a1014 chip=0x1014168c rev=0x01 hdr=0x00
    vendor     = 'Atheros Communications Inc.'
    device     = 'AR5212 Atheros AR5212 802.11abg wireless'
    class      = network
    subclass   = ethernet
....

以上輸出資訊說明 [.filename]#ath# 驅動程式已經找到一個無線乙太網路裝置。

在 man:man[1] 指令加上 `-k` 旗標可提供有用的資訊，例如，這可列出有包含指定裝置品牌或名稱的手冊頁面清單：

[source,shell]
....
# man -k Atheros
ath(4)                   - Atheros IEEE 802.11 wireless network driver
ath_hal(4)               - Atheros Hardware Access Layer (HAL)
....

準備好硬體清單之後，參考該清單來確認已安裝的硬體驅動程式在編輯自訂核心設定時沒有被移除。

[[kernelconfig-config]]
== 設定檔

為了要建立自訂核心設定檔並編譯自訂核心，必須先安裝完整的 FreeBSD 原始碼樹。

若 [.filename]#/usr/src/# 目錄不存在或者是空的，代表尚未安裝。原始碼可以使用 Subversion 並依據 crossref:mirrors[svn,使用 Subversion] 中的操作說明來安裝。

完成原始碼安裝完成後，需檢查 [.filename]#/usr/src/sys# 內的檔案。該目錄內包含數個子目錄，這些子目錄代表著支援的硬體架構 (Architecture) 如下：[.filename]#amd64#, [.filename]#i386#, [.filename]#ia64#, [.filename]#powerpc# 以及 [.filename]#sparc64#。在指定架構目錄中的內容只對該架構有效，其餘部份的程式碼與硬體架構無關，可通用所有平台。每個支援的硬體架構中會有 [.filename]#conf# 子目錄，裡面含有供該架構使用的 [.filename]#GENERIC# 核心設定檔。

請不要直接對 [.filename]#GENERIC# 檔案做編輯。複製該檔案為另一個名稱，並對複製出來的檔案做編輯，習慣上檔名會全部使用大寫字元。當維護多台安裝不同的硬體的 FreeBSD 機器時，將檔名後方加上機器的主機名稱 (Host name) 是個不錯的方法。以下範例使用 `amd64` 架構的 [.filename]#GENERIC# 設定檔建立了一個複本名稱為 [.filename]#MYKERNEL#：

[source,shell]
....
# cd /usr/src/sys/amd64/conf
# cp GENERIC MYKERNEL
....

現在可以使用任何 ASCII 文字編輯器來自訂 [.filename]#MYKERNEL#。預設的編輯器為 vi，在 FreeBSD 也內建一個易於初學者使用的編輯器叫做 ee。

核心設定檔的格式很簡單，每一行會含有代表裝置 (Device) 或子系統 (Subsystem) 的關鍵字、參數以及簡短的說明。任何在 `#` 符號之後的文字會被當做註解並且略過。要移除核心對某個裝置或子系統的支援，僅需要在代表該裝置或子系統的行前加上 `#` 符號。請不要在您還不了解用途的行前加上或移除 `#` 符號。

[WARNING]
====

移除對裝置或選項的支援很容易會造成核心損壞。例如，若從核心設定檔 man:ata[4] 驅動程式，那麼使用 ATA 磁碟驅動程式的系統便會無法開機。因此當您不確定時，請在核心保留該項目的支援。
====

除了在設定檔中提供的簡短說明之外，尚有其他的說明在 [.filename]#NOTES# 檔案中，可在與該架構 [.filename]#GENERIC# 相同的目錄底下找到。要查看所有架構通用的選項，請參考 [.filename]#/usr/src/sys/conf/NOTES#。

[TIP]
====

當完成自訂的核心設定檔，請備份到 [.filename]#/usr/src# 位置之外。

或者，將核心設定檔放在其他地方，然後建立一個符號連結 (Symbolic link) 至該檔案：

[source,shell]
....
# cd /usr/src/sys/amd64/conf
# mkdir /root/kernels
# cp GENERIC /root/kernels/MYKERNEL
# ln -s /root/kernels/MYKERNEL
....

====

設定檔中可以使用 `include` 指令 (Directive)。該指令可以引用其他設定檔到目前的設定檔，這讓只需根據現有檔案設定做些微調整時更簡單。若只有少量的額外選項或驅動程式需要設定，該指令可引用 [.filename]#GENERIC# 並設定額外增加的選項，如範例所示：

[.programlisting]
....
include GENERIC
ident MYKERNEL

options         IPFIREWALL
options         DUMMYNET
options         IPFIREWALL_DEFAULT_TO_ACCEPT
options         IPDIVERT
....

使用此方法，設定檔只含有與 [.filename]#GENERIC# 核心不同的部份。當升級有新功能加入 [.filename]#GENERIC# 時，也可一併引用，除非特別使用 `nooptions` 或 `nodevice` 選項來排除設定。更詳細的設定檔指令及其說明可在 man:config[5] 找到。

[NOTE]
====
要產生含有所有可用選項的設定檔，可以 `root` 執行以下指令：

[source,shell]
....
# cd /usr/src/sys/arch/conf && make LINT
....

====

[[kernelconfig-building]]
== 編譯與安裝自訂核心

完成自訂設定檔的編輯並儲存之後，便可依據以下步驟編譯核心的原始碼：

[.procedure]
====
*Procedure: 編譯核心*

. 切換至此目錄：
+
[source,shell]
....
# cd /usr/src
....
+
. 指定自訂核心設定檔的名稱來編譯新的核心：
+
[source,shell]
....
# make buildkernel KERNCONF=MYKERNEL
....
+
. 安裝使用指定核心設定檔所編譯的新核心。此指令將會複製新核心到 [.filename]#/boot/kernel/kernel# 並將舊核心備份到 [.filename]#/boot/kernel.old/kernel#：
+
[source,shell]
....
# make installkernel KERNCONF=MYKERNEL
....
+
. 關機並重新開機載入新的核心，若發生錯誤請參考 crossref:kernelconfig[kernelconfig-noboot,無法使用核心開機]。
====

預設在自訂核心編譯完成後，所有核心模組也同被重新編譯。要快速更新核心或只編譯自訂的模組，需在開始編譯之前先編輯 [.filename]#/etc/make.conf#。

例如，使用以下變數可指定要編譯的模組清單來替代預設編譯所有模組的設定：

[.programlisting]
....
MODULES_OVERRIDE = linux acpi
....

或者，可使用以下變數來從編譯程序中排除要編譯的模組：

[.programlisting]
....
WITHOUT_MODULES = linux acpi sound
....

尚有其他可用的變數，請參考 man:make.conf[5] 取得詳細資訊。

[[kernelconfig-trouble]]
== 如果發生錯誤

當編譯自訂核心時可能發生以下四種類型的問題：

`config` 失敗::
若 `config` 失敗，會列出不正確的行號。使用以下訊息為例子，需要與 [.filename]#GENERIC# 或 [.filename]#NOTES# 比對來確認第 17 行輸入的內容正確：
+
[source,shell]
....
config: line 17: syntax error
....

`make` 失敗::
若 `make` 失敗，通常是因為核心設定檔未提供足夠的資訊讓 `config` 找到問題。請仔細檢查設定檔，若仍不清楚問題，請寄發電子郵件給 http://lists.FreeBSD.org/mailman/listinfo/freebsd-questions[FreeBSD general questions mailing list] 並附上核心設定檔。

[[kernelconfig-noboot]]
無法使用核心開機::
若新核心無法開機或無法辨識裝置並不要恐慌！幸好，FreeBSD 有良好的機制可以從不相容的核心復原。只需要在 FreeBSD 開機載入程式 (Boot loader) 選擇要用來開機的核心便可，當系統開機選單出現時選擇 "Escape to a loader prompt" 選項，並在指令提示後輸入 `boot _kernel.old_` 或替換為任何其他已經知道可以正常開機的核心名稱。
+
使用好的核心開機之後，檢查設定檔並嘗試再編譯一次。[.filename]#/var/log/messages# 是有用的資源，它在每次成功開機時會記錄核心訊息。同樣的，man:dmesg[8] 也會印出自本次開機後的核心訊息。
+
[NOTE]
====
在排除核心問題時，請確定留有 [.filename]#GENERIC# 的複本，或者其他已知可以運作的核心，並使用不同的名稱來確保下次編譯時不會被刪除，這很重要，因此每當新的核心被安裝之後，[.filename]#kernel.old# 都會被最後安裝的核心覆寫，有可能會無法開機。盡快，透過重新命名將可運作的核心目錄移動到目前運作的核心目錄：

[source,shell]
....
# mv /boot/kernel /boot/kernel.bad
# mv /boot/kernel.good /boot/kernel
....

====

核心可運作，但 man:ps[1] 無法運作::
若核心版本與系統工具所編譯的版本不同，例如，有一個核心使用 -CURRENT 的原始碼編譯並安裝在 -RELEASE 的系統上，許多系統狀態指令如 man:ps[1] 及 man:vmstat[8] 將會無法運作。要修正此問題，請使用與核心相同版本的原始碼樹 (Source tree) crossref:cutting-edge[makeworld,重新編譯並安裝 World]。使用與作業系統其他部份版本不同的核心永遠不會是個好主意。
