前段時(shí)間對(duì)內(nèi)核的模塊重新做了研究,對(duì)內(nèi)核模塊的編譯流程也作了一定的了解,比起5年前有更深入的認(rèn)識(shí)。
根據(jù)LDD3的內(nèi)核模塊makefile和原理說(shuō)明,我根據(jù)自己的需要做了適當(dāng)?shù)男薷氖沟眠@個(gè)Makefile腳本可以方便被應(yīng)用于不同的簡(jiǎn)單模塊編譯,并可以在模塊需要編譯進(jìn)內(nèi)核的時(shí)候直接放入內(nèi)核源碼目錄中,腳本如下:
- MODULE_NAME = hello_linux_simple
- MODULE_CONFIG = CONFIG_HELLO_LINUX_SIMPLE
- CROSS_CONFIG = y
- # Comment/uncomment the following line to disable/enable debugging
- DEBUG = y
- ifneq ($(KERNELRELEASE),)
- # Add your debugging flag (or not) to CFLAGS
- ifeq ($(DEBUG),y)
- DEBFLAGS = -O -g -DDEBUG # "-O" is needed to expand inlines
- else
- DEBFLAGS = -O2
- endif
- ccflags-y += $(DEBFLAGS)
- obj-$($(MODULE_CONFIG)) := $(MODULE_NAME).o
- #for Multi-files module
- $(MODULE_NAME)-objs := hello_linux_simple_dep.o ex_output.o
- else
- ifeq ($(CROSS_CONFIG), y)
- #for Cross-compile
- KERNELDIR = (內(nèi)核源碼路徑)
- ARCH = arm
- #FIXME:maybe we need absolute path for different user. eg root
- #CROSS_COMPILE = arm-none-linux-gnueabi-
- CROSS_COMPILE = (交叉編譯工具路徑)
- INSTALLDIR := (目標(biāo)模塊所安裝的根文件系統(tǒng)路徑)
- else
- #for Local compile
- KERNELDIR = /lib/modules/$(shell uname -r)/build
- ARCH = x86
- CROSS_COMPILE =
- INSTALLDIR := /
- endif
- ################################
- PWD := $(shell pwd)
- .PHONY: modules modules_install clean
- modules:
- $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) $(MODULE_CONFIG)=m -C $(KERNELDIR) M=$(PWD) modules
- modules_install: modules
- $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) $(MODULE_CONFIG)=m -C $(KERNELDIR) INSTALL_MOD_PATH=$(INSTALLDIR) M=$(PWD) modules_install
- clean:
- @rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers *.order .*.o.d modules.builtin
- endif
這個(gè)腳本與模塊源碼放置于同一個(gè)目錄。針對(duì)不同的模塊,只要簡(jiǎn)單的修改部分參數(shù),使用時(shí)只需要在該目錄下執(zhí)行一個(gè)簡(jiǎn)單的“make”命令即可。下面我簡(jiǎn)單分析一下。
上面的示例腳本利用了擴(kuò)展的 GNU make 語(yǔ)法。這個(gè) makefile 在編譯內(nèi)核模塊的時(shí)候會(huì)要被讀取 2 次。
第一次:當(dāng)從命令行執(zhí)行“make”命令時(shí),“make”會(huì)調(diào)用這個(gè)makefile。此時(shí)由于“KERNELRELEASE”變量沒(méi)有被設(shè)置,所以會(huì)執(zhí)行“else”的部分,也就是“modules”目標(biāo)下的指令,類似我們上面講的的編譯命令“make -C $() M=$() modules”。只不過(guò)這里為了通用性添加了一些變量而已。
第二次:當(dāng)執(zhí)行了上面的指令后,make 命令( 在 makefile 里參數(shù)化成 $(MAKE))就會(huì)像調(diào)用內(nèi)核編譯系統(tǒng)。再次讀取這個(gè)makefile。由于內(nèi)核編譯系統(tǒng)設(shè)置了“KERNELRELEASE”變量,所以此次內(nèi)核編譯系統(tǒng)看到了“obj-$($(MODULE_CONFIG)) := $(MODULE_NAME).o”也就是類似之前我們描述的“obj-m”。這樣內(nèi)核的編譯系統(tǒng)就可以完成實(shí)際的模塊編譯工作。
這種模塊編譯Makefile只需做很小的改動(dòng)就可以方便的應(yīng)用于不同的模塊中。對(duì)于不同的模塊你可能需要修改:
- MODULE_NAME = (模塊名)
- MODULE_CONFIG = (在模塊編譯進(jìn)內(nèi)核時(shí)的配置選項(xiàng))
- CROSS_CONFIG = y(是否為交叉編譯)
- DEBUG = y (是否定義調(diào)試標(biāo)志)
- ......
- $(MODULE_NAME)-objs := (若為多文件模塊,則在此列出。否則用#屏蔽)
- ......
- ifeq ($(CROSS_CONFIG), y)
- #for Cross-compile
- KERNELDIR = (內(nèi)核源碼路徑)
- ARCH = arm(交叉編譯時(shí),目標(biāo)CPU構(gòu)架名,此處為arm)
- #FIXME:maybe we need absolute path for different user. eg root
- #CROSS_COMPILE = arm-none-linux-gnueabi-
- CROSS_COMPILE = (交叉編譯工具路徑及前綴)
- INSTALLDIR := (目標(biāo)模塊所安裝的根文件系統(tǒng)路徑)
- else
- #for Local compile
- ......
- ARCH = x86(這個(gè)根據(jù)本地構(gòu)架可能需要修改)
- ......
- endif
對(duì)于這個(gè)Makefile,還有一點(diǎn)就是考慮到直接放入內(nèi)核目錄,編譯進(jìn)內(nèi)核的情況。如果只是簡(jiǎn)單的模塊,可以再次利用這個(gè)Makefile。這就是為什么上面的Makefile比較繁瑣,因?yàn)樗瑫r(shí)支持直接放入內(nèi)核源碼樹(shù)中使用。
假設(shè)我們將一個(gè)名為hello_linux_simple的模塊編譯入內(nèi)核中,我們需要做的工作就是將包含以上Makefile和源碼的目錄拷貝到一個(gè)目錄(例如drivers/misc)下,并適當(dāng)修改該目錄下的內(nèi)核編譯系統(tǒng)Kconfig和Makefile文件:
在(drivers/misc/)Kconfig中添加:
- config HELLO_LINUX_SIMPLE
- tristate "simple hello_linux module"
- # depends on
- help
- simple hello_linux module
由于此模塊不依賴其他模塊,“depends on”就可以屏蔽了。
在(drivers/misc/)Makefile中添加:
- obj-$(CONFIG_HELLO_LINUX_SIMPLE) += hello_linux_simple/
注意:上面的藍(lán)字必須要和模塊源碼Makefile中的MODULE_CONFIG的值一致。
文件修改好后,就可以配置內(nèi)核了。在內(nèi)核的make menuconfig中,我們可以看到:
- Device Drivers -→
- [*] Misc devices --->
- < > simple hello_linux module
既可以用“M”編譯成模塊,也可以用“Y”編譯進(jìn)內(nèi)核中。
關(guān)于調(diào)試選項(xiàng)DEBUG
上面定義了DEBUG=y的選項(xiàng),是為了在調(diào)試的時(shí)候啟用pr_debug和pr_devel宏,這些宏是printk的封裝(參見(jiàn)《內(nèi)核日志及printk結(jié)構(gòu)淺析》),或者可以開(kāi)啟其他依賴DEBUG定義的宏。這樣在調(diào)試結(jié)束之后就可以方便的通過(guò)屏蔽#DEBUG=y來(lái)關(guān)閉調(diào)試信息的輸出,不產(chǎn)生調(diào)試信息代碼。