在学习使用内核中的驱动模块时,可能需要修改一些源代码,跟踪调试等,这就需要重新编译该模块,一种方式是直接重新编译内核,但是这样耗时较多,而且每次修改的时候都需要这样做就太麻烦了。所以这里介绍另一中方式,不用重新编译内核,只用修改该驱动模块的Makefile,使得该驱动模块能够不依赖编译内核而单独编译,下面以软raid模块为例来进行说明。
首先需要确保当前内核版本与系统中的linux-headers是一致的。查看方式是通过uname -r查看到的内核与/usr/src/linux-headers-xxx是相同的版本,而且还必须存在/lib/modules/`uname -r`/build这个软链接。
#uname -r 3.11.0-14-generic #ls /usr/src/ /usr/src/linux-headers-3.11.0-14-generic #ls /lib/modules/`uname -r`/build -la lrwxrwxrwx 1 root root 40 11月 13 01:40 /lib/modules/3.11.0-14-generic/build -> /usr/src/linux-headers-3.11.0-14-generic然后就是修改driver/md/Makefile。原本的Makefile如下:
dm-mod-y += dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \ dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o dm-multipath-y += dm-path-selector.o dm-mpath.o dm-snapshot-y += dm-snap.o dm-exception-store.o dm-snap-transient.o \ dm-snap-persistent.o dm-mirror-y += dm-raid1.o dm-log-userspace-y \ += dm-log-userspace-base.o dm-log-userspace-transfer.o dm-thin-pool-y += dm-thin.o dm-thin-Metadata.o dm-cache-y += dm-cache-target.o dm-cache-Metadata.o dm-cache-policy.o dm-cache-mq-y += dm-cache-policy-mq.o dm-cache-cleaner-y += dm-cache-policy-cleaner.o md-mod-y += md.o bitmap.o raid456-y += raid5.o # Note: link order is important. All raid personalities # and must come before md.o,as they each initialise # themselves,and md.o may use the personalities when it # auto-initialised. obj-$(CONFIG_MD_LINEAR) += linear.o obj-$(CONFIG_MD_RAID0) += raid0.o obj-$(CONFIG_MD_RAID1) += raid1.o obj-$(CONFIG_MD_RAID10) += raid10.o obj-$(CONFIG_MD_RAID456) += raid456.o obj-$(CONFIG_MD_MULTIPATH) += multipath.o obj-$(CONFIG_MD_FAULTY) += faulty.o obj-$(CONFIG_BCACHE) += bcache/ obj-$(CONFIG_BLK_DEV_MD) += md-mod.o obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o obj-$(CONFIG_DM_BUFIO) += dm-bufio.o obj-$(CONFIG_DM_BIO_PRISON) += dm-bio-prison.o obj-$(CONFIG_DM_CRYPT) += dm-crypt.o obj-$(CONFIG_DM_DELAY) += dm-delay.o obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o obj-$(CONFIG_DM_SWITCH) += dm-switch.o obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o obj-$(CONFIG_DM_PERSISTENT_DATA) += persistent-data/ obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o dm-region-hash.o obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o obj-$(CONFIG_DM_ZERO) += dm-zero.o obj-$(CONFIG_DM_RAID) += dm-raid.o obj-$(CONFIG_DM_THIN_PROVISIONING) += dm-thin-pool.o obj-$(CONFIG_DM_VERITY) += dm-verity.o obj-$(CONFIG_DM_CACHE) += dm-cache.o obj-$(CONFIG_DM_CACHE_MQ) += dm-cache-mq.o obj-$(CONFIG_DM_CACHE_CLEANER) += dm-cache-cleaner.o ifeq ($(CONFIG_DM_UEVENT),y) dm-mod-objs += dm-uevent.o endif修改之后的Makefile为:
LINUXROOT=/lib/modules/$(shell uname -r)/build all: make -C $(LINUXROOT) SUBDIRS=`pwd` $(EXTRA_CFLAGS) KBUILD_VERBOSE=1 modules clean: @rm .*.cmd -rf @rm .tmp* -rf @rm *.o -rf @rm *.mod.* -rf @rm *.ko -rf @echo "Delete output files" obj-m += raid1.o obj-m += raid10.o obj-m += raid456.o obj-m += md-mod.o md-mod-objs := md.o bitmap.o raid456-objs := raid5.o有时需要连接内核源代码外部的系统头文件,但Kbuild 系统默认的系统头文件都在内核源代码内部,如何使用外部的头文件呢?这个可以借助于Kbuild 系统的特殊规则: EXTRA_CFLAGS。 EXTRA_CFLAGS 可以给Kbuild 系统添加外部系统头文件。其中的KBUILD_VERBOSE设置kbuild输出详细信息。
从上面的例子可以看出把内核驱动单独拿出来编译也不是很困难的事情,修改对应的Makefile时候使用obj-m += xxx.o来指定要生成的模块,如果一个模块是由多个.o文件一起编译而来的,则可以如下指定。
obj-m += xxx.o xxx-objs := xxx.o yyy.o zzz.o
直接使用insmod raid1.ko可以加载成功,但是insmod raid456.ko时却报错:
Error: could not insert module raid456.ko: Unknown symbol in module使用dmesg查看内核日志:
[303699.452369] raid5: Unknown symbol async_gen_syndrome (err 0) [303699.452426] raid5: Unknown symbol async_tx_quiesce (err 0) [303699.452445] raid5: Unknown symbol async_xor (err 0) [303699.452459] raid5: Unknown symbol async_xor_val (err 0) [303699.452484] raid5: Unknown symbol async_memcpy (err 0) [303699.452506] raid5: Unknown symbol async_trigger_callback (err 0) [303699.452515] raid5: Unknown symbol async_raid6_2data_recov (err 0) [303699.452553] raid5: Unknown symbol async_syndrome_val (err 0) [303699.452585] raid5: Unknown symbol async_raid6_datap_recov (err 0)从出错信息中得知raid456这个模块依赖其他模块,而其他模块间也有可能存在依赖关系,这样就会存在加载顺序的要求,如果一个一个试,很麻烦,不过,这种遇到这种模块依赖时,就可以使用modprobe <module-name>来加载。注意:使用modprobe加载时,后面的模块名不能带有.ko后缀。 这里再执行以下modprobe raid456就可以成功了。