Linux 內核配置系統由 Kconfig(配置選項定義)、Makefile(建構規則)、配置工具 三大部分組成。本文聚焦於 Makefile 架構、核心變數、Rules.make 共用規則、交叉編譯變數與 ARM 架構實例,幫助開發者理解內核建構流程並撰寫/維護驅動模組 Makefile。
Linux Kernel Source Tree
├── Makefile # 頂層總控 Makefile
├── .config # 配置結果 (make menuconfig 產生)
├── arch/arm/Makefile # ARM 架構專屬 Makefile
├── drivers/Makefile # 子系統 Makefile
├── Rules.make # 共用編譯規則
└── scripts/kconfig/ # 配置工具原始碼
.config 產生配置變數 (CONFIG_*)。SUBDIRS 變數決定編譯哪些目錄。vmlinux(內核映像)與模組 (.ko)。VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 32
EXTRAVERSION = -custom
KERNELRELEASE = 2.6.32-custom
ARCH := arm # 目標架構
CROSS_COMPILE := arm-linux-gnueabi- # 交叉編譯工具鏈前綴
頂層 Makefile 含 include arch/$(ARCH)/Makefile 引入架構專屬定義。
TOPDIR / srctree:源碼根目錄絕對路徑。SUBDIRS:需編譯的子目錄列表,頂層預設 kernel drivers mm fs net ipc lib,架構 Makefile 會追加架構專屬目錄(如 arch/arm/kernel、arch/arm/mm)。HEAD # 最先鏈入的啟動代碼 (arch/arm/kernel/head-$(PROCESSOR).o)
CORE_FILES # 核心子系統: kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o + 架構擴充
DRIVERS # 驅動目標文件集合
NETWORKS # 網路協議棧目標文件
LIBS # 通用庫文件 (lib/lib.a + 架構專屬 lib.a)
鏈接規則:
vmlinux: $(HEAD) init/main.o init/version.o $(CORE_FILES) $(DRIVERS) $(NETWORKS) $(LIBS)
$(LD) $(LINKFLAGS) ... -o vmlinux
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
AS = $(CROSS_COMPILE)as
AR = $(CROSS_COMPILE)ar
CPP = $(CC) -E
CFLAGS := -Wall -Wundef -Wstrict-prototypes -O2 -fomit-frame-pointer
LINKFLAGS := -p -X -T arch/arm/vmlinux.lds # 架構專屬鏈接腳本
來源於 .config,值為 y (內建)、m (模組)、n (不選)、空 (未定義)。
Makefile 中透過 obj-$(CONFIG_FOO) += foo.o 實現條件編譯。
每個子目錄 Makefile 末尾 include $(TOPDIR)/Rules.make 引入共用規則。
| 變數 | 含義 | 後綴說明 |
|---|---|---|
O_OBJS | 編入 vmlinux 的內建目標文件 | |
OX_OBJS | 同上,但使用 EXPORT_SYMBOL 導出符號 | X = Export |
L_OBJS | 編入 vmlinux 的庫目標文件 (經 ld -r 合併) | |
LX_OBJS | 同上,導出符號 | |
M_OBJS | 編譯為可載入模組 (.ko) 的目標文件 | |
MX_OBJS | 同上,導出符號 |
O_TARGET:.o 結尾,ld -r 合併 O_OBJS+OX_OBJS 產生。L_TARGET:.a 結尾,靜態庫格式,合併 L_OBJS+LX_OBJS 產生。頂層 Makefile 遞迴收集各目錄的 O_TARGET/L_TARGET 組成上述 CORE_FILES、DRIVERS 等大塊。
arch/arm/Makefile)
# 機器專屬目錄
MACHINE := versatile
MACHDIR := arch/arm/mach-$(MACHINE)
ifeq ($(MACHDIR),$(wildcard $(MACHDIR)))
SUBDIRS += $(MACHDIR)
CORE_FILES := $(MACHDIR)/$(MACHINE).o $(CORE_FILES)
endif
HEAD := arch/arm/kernel/head-$(PROCESSOR).o arch/arm/kernel/init_task.o
SUBDIRS += arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/nwfpe
CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES)
LIBS := arch/arm/lib/lib.a $(LIBS)
LINKFLAGS := -p -X -T arch/arm/vmlinux.lds
關鍵點:
① 動態加入機器專屬目錄到 SUBDIRS 與 CORE_FILES。
② 定義 HEAD 確保啟動代碼最先鏈接。
③ 追加架構專屬核心目錄與庫。
④ 指定鏈接腳本 vmlinux.lds 控制記憶體佈局。
# Makefile for my_driver.ko
obj-m := my_driver.o
my_driver-y := main.o hw.o utils.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
交叉編譯時:make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-。
.config 為核心,透過 CONFIG_* 變數控制條件編譯。Rules.make 統一編譯規則,子目錄透過 O_OBJS/M_OBJS 等變數聲明產物歸屬。