解析 Linux 内核可装载模块的版本检查机制(2)

模块的装载与卸载

上述中,我们在装载模块时使用了工具 insmod。在 Linux 2.6 中,工具 insmod 被重新设计并作为工具集 module-init-tools 中的一个程序,其通过系统调用 sys_init_module(您可查看头文件 include/asm-generic/unistd.h)衔接了模块的版本检查,模块的装载等功能(如 图 3所示)。module-init-tools 是为 2.6 内核设计的运行在 Linux 用户空间的模块装卸载工具集,其包含的程序 rmmod 用于卸载当前内核中的模块。


图 3. 模块的装卸载

图 3. 模块的装卸载



表 1. 工具集 module-init-tools 中的部分程序

名称说明
insmod   装载模块到当前运行的内核中  
rmmod   从当前运行的内核中卸载模块  
lsmod   显示当前内核已加装的模块信息  
modinfo   检查与内核模块相关联的目标文件,并打印出所有得到的信息  
modprobe   利用 depmod 创建的依赖关系文件自动加载相关的模块  
depmod   创建一个内核可装载模块的依赖关系文件,modprobe 用它来自动加载模块  

值得一提的是在 module-init-tools 中可用于模块装卸载的程序 modprobe。程序 modprobe 的内部函数调用过程正如您所想与 insmod 类似,只是其装载过程会查找一些模块装载的配置文件,且 modprobe 在装载模块时可解决模块间的依赖性,即若有必要,程序 modprobe 会在装载一个模块时自动加载该模块依赖的其他模块。

其他一些细节

从用户空间装载模块到内核时,Linux 还对用户权限进行了检查。模块的装载须是获得 CAP_SYS_MODULE 权限的超级用户,这正是模块装载时最先检查的内容(见 图 2)。在 Linux 2.6 中,模块在构建时生成了一些临时文件,如 .o 文件、.mod.o 文件等。了解这些文件的生成有助于我们更好的理解 Linux 2.6 的内核模块构建过程以及版本信息的检查等内容。文件 .o 是模块代码(即 .c 文件)经编译后获得的目标文件,文件 .mod.o 则对应文件 .mod.c。文件 <module>.mod.c 是对 <modulue>.c 的扩展,清单 8展示了文件 kobject-example.mod.c 的内容 ( 即模块 kobject-example.ko 的 .mod.c 文件 ),您可见到与模块版本检查相关三个小节。


清单 8. 文件 kobject-example.mod.c

# cat ./kobject/kobject-example.mod.c ... MODULE_INFO(vermagic, VERMAGIC_STRING); struct module __this_module __attribute__((section(".gnu.linkonce.this_module"))) = { ... }; static const struct modversion_info ____versions[] __used __attribute__((section("__versions"))) = { ... }; static const char __module_depends[] __used __attribute__((section(".modinfo"))) = "depends="; MODULE_INFO(srcversion, "B06F9B8B7AB52AEED247B9F");  

清单 8 中显示了模块 kobject-example.ko 中的三个 section 以及宏 MODULE_INFO,最后一行 srcversion 则需开启内核配置选项 MODULE_SRCVERSION_ALL。经上述,我们知道这三个 section 正是模块版本检查的附加信息。我们通过工具 objdump 查看 .modinfo 小节(见 清单 9, 即模块的 vermagic 信息)。<module>.ko 的附加信息合并自文件 <module>.o 与文件 <module>.mod.o。内核工具 modpost 完成了一这步骤,且该工具是 Linux 2.6 内核模块构建时所必须的。


清单 9. 使用工具 objdump 查看 .modinfo 小节

# objdump --section=.modinfo -s hello/hello.o hello/hello.o: file format elf64-x86-64 Contents of section .modinfo: 0000 6c696365 6e73653d 4475616c 20425344 license=Dual BSD 0010 2f47504c 00 /GPL. # modinfo hello/hello.o filename: hello/hello.o license: Dual BSD/GPL # objdump --section=.modinfo -s hello/hello.mod.o ... # objdump --section=.modinfo -s hello/hello.ko ...  

经上述,我们可知内核树的顶层 Makefile 文件包含了内核版本的信息,且该信息经编译后被添加到模块的(头文件 include/generated/utsrelease.h 保存的内核版本信息来自顶层 Makefile)。表 1中,工具 lsmod 打开文件 /proc/modules 查询当前内核中已装载的模块(见清单 10),文件 /proc/modules 还被 rmmod 在卸载模块时使用。另外,若您在装载模块 hello.ko 后没能在终端下看到相应的字符串输出,则需检查文件 /proc/sys/kernel/printk,并重设下消息级别。


清单 10. 工具 lsmod 的使用

# insmod ./kobject/kobject-example.ko # ls /sys/kernel/kobject_example/ bar baz foo # lsmod | grep kobject kobject_example 12857 0 # cat /proc/modules | grep kobject kobject_example 12857 0 - Live 0xffffffffa0523000  

Linux 2.6 构建模块时工具 modpost 被 scripts/Makefile.modpost 调用,生成 <module>.mod.c 及文件 Module.symvers(见 清单 15)。在开启内核选项 CONFIG_MODVERSIONS 之后,文件 Makefile.Build 会调用工具 genksyms(现位于内核树 scripts/genksyms 目录下,在 Linux 2.4 时是模块工具集 Modutils 的一部分)生成 CRC 信息(见 清单 11)。其中代码 call cmd_gensymtypes 就是对工具 genksyms 的调用。另外一个较为明晰的方式是,使用工具 objdump 或 readelf 查看相关的 ELF 小节,并使用 make – n 查看模块构建过程。


清单 11. 文件 Makefile.Build 的部分内容

ifndef CONFIG_MODVERSIONS cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< else cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $< cmd_modversions = \ if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \ $(call cmd_gensymtypes, $(KBUILD_SYMTYPES)) \ > $(@D)/.tmp_$(@F:.o=.ver); \ \ $(LD) $(LDFLAGS) -r -o $@ $(@D)/.tmp_$(@F) \ -T $(@D)/.tmp_$(@F:.o=.ver); \ rm -f $(@D)/.tmp_$(@F) $(@D)/.tmp_$(@F:.o=.ver); \ else \ mv -f $(@D)/.tmp_$(@F) $@; \ fi; endif  

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/4554eb2a2d4589043e3ac8ef5b34832d.html