前段時間對內核的模塊重新做了研究,對內核模塊的編譯流程也作了一定的了解,比起5年前有更深入的認識。
根據LDD3的內核模塊makefile和原理說明,我根據自己的需要做了適當的修改使得這個Makefile腳本可以方便被應用于不同的簡單模塊編譯,并可以在模塊需要編譯進內核的時候直接放入內核源碼目錄中,腳本如下:
- 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 = (內核源碼路徑)
- ARCH = arm
- #FIXME:maybe we need absolute path for different user. eg root
- #CROSS_COMPILE = arm-none-linux-gnueabi-
- CROSS_COMPILE = (交叉編譯工具路徑)
- INSTALLDIR := (目標模塊所安裝的根文件系統(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
這個腳本與模塊源碼放置于同一個目錄。針對不同的模塊,只要簡單的修改部分參數,使用時只需要在該目錄下執(zhí)行一個簡單的“make”命令即可。下面我簡單分析一下。
上面的示例腳本利用了擴展的 GNU make 語法。這個 makefile 在編譯內核模塊的時候會要被讀取 2 次。
第一次:當從命令行執(zhí)行“make”命令時,“make”會調用這個makefile。此時由于“KERNELRELEASE”變量沒有被設置,所以會執(zhí)行“else”的部分,也就是“modules”目標下的指令,類似我們上面講的的編譯命令“make -C $() M=$() modules”。只不過這里為了通用性添加了一些變量而已。
第二次:當執(zhí)行了上面的指令后,make 命令( 在 makefile 里參數化成 $(MAKE))就會像調用內核編譯系統(tǒng)。再次讀取這個makefile。由于內核編譯系統(tǒng)設置了“KERNELRELEASE”變量,所以此次內核編譯系統(tǒng)看到了“obj-$($(MODULE_CONFIG)) := $(MODULE_NAME).o”也就是類似之前我們描述的“obj-m”。這樣內核的編譯系統(tǒng)就可以完成實際的模塊編譯工作。
這種模塊編譯Makefile只需做很小的改動就可以方便的應用于不同的模塊中。對于不同的模塊你可能需要修改:
- MODULE_NAME = (模塊名)
- MODULE_CONFIG = (在模塊編譯進內核時的配置選項)
- CROSS_CONFIG = y(是否為交叉編譯)
- DEBUG = y (是否定義調試標志)
- ......
- $(MODULE_NAME)-objs := (若為多文件模塊,則在此列出。否則用#屏蔽)
- ......
- ifeq ($(CROSS_CONFIG), y)
- #for Cross-compile
- KERNELDIR = (內核源碼路徑)
- ARCH = arm(交叉編譯時,目標CPU構架名,此處為arm)
- #FIXME:maybe we need absolute path for different user. eg root
- #CROSS_COMPILE = arm-none-linux-gnueabi-
- CROSS_COMPILE = (交叉編譯工具路徑及前綴)
- INSTALLDIR := (目標模塊所安裝的根文件系統(tǒng)路徑)
-
- else
- #for Local compile
- ......
- ARCH = x86(這個根據本地構架可能需要修改)
- ......
- endif
對于這個Makefile,還有一點就是考慮到直接放入內核目錄,編譯進內核的情況。如果只是簡單的模塊,可以再次利用這個Makefile。這就是為什么上面的Makefile比較繁瑣,因為他同時支持直接放入內核源碼樹中使用。
假設我們將一個名為hello_linux_simple的模塊編譯入內核中,我們需要做的工作就是將包含以上Makefile和源碼的目錄拷貝到一個目錄(例如drivers/misc)下,并適當修改該目錄下的內核編譯系統(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/
注意:上面的藍字必須要和模塊源碼Makefile中的MODULE_CONFIG的值一致。
文件修改好后,就可以配置內核了。在內核的make menuconfig中,我們可以看到:
- Device Drivers -→
- [*] Misc devices --->
- < > simple hello_linux module
既可以用“M”編譯成模塊,也可以用“Y”編譯進內核中。
-
關于調試選項DEBUG 上面定義了DEBUG=y的選項,是為了在調試的時候啟用pr_debug和pr_devel宏,這些宏是printk的封裝(參見《內核日志及printk結構淺析》),或者可以開啟其他依賴DEBUG定義的宏。這樣在調試結束之后就可以方便的通過屏蔽#DEBUG=y來關閉調試信息的輸出,不產生調試信息代碼。
|