260 likes | 558 Views
Linux Kernel Module. 中科院计算所软件室向泓 2000.11. 目录. 概述 LKM 的实现 LKM 编程. 概述. Linux 是 一个单体内核的操作系统; Kernel Module 是对内核的扩充,可动态加载和卸载;. LKM 的实现. 连接和载入( Linking & Loading) ,ELF 格式 相关内核数据结构,系统调用, kerneld & kmod modutils. Linking & Loading. 三个基本要求: 符号解析( Symbol Resolution) ; 重定位( Relocation) ;
E N D
Linux Kernel Module 中科院计算所软件室向泓 2000.11
目录 • 概述 • LKM的实现 • LKM编程
概述 • Linux是一个单体内核的操作系统; • Kernel Module是对内核的扩充,可动态加载和卸载;
LKM的实现 • 连接和载入(Linking & Loading),ELF格式 • 相关内核数据结构,系统调用,kerneld & kmod • modutils
Linking & Loading • 三个基本要求: • 符号解析(Symbol Resolution); • 重定位(Relocation); • 程序载入(Program Loading); • Linker和Loader的功能划分: • Linker:符号解析,重定位; • Loader:重定位,程序载入;
文件格式 • COM; • A.OUT; • EXE; • COFF; • ELF; • PE; • OMF;
Executable and Linkable Format • ELF是Linux的标准二进制文件格式; • 更方便的实现共享库和动态连接; • Sections & Segments • SHT_PROGBITS • SHT_SYMTAB • SHT_STRTAB • SHT_REL(SHT_RELA)
struct module(1) • struct module { • unsigned long size_of_struct; • struct module *next; • const char * name; • unsigned long size; • union { • atomic_t usecount; • long pad; • } uc; • unsigned long flags; • unsigned nsyms; • unsigned ndeps;
struct module(2) • struct module_symbol *syms; • struct module_ref *deps; • struct module_ref *refs; • int (*init)(void); • void (*cleanup)(void); • const struct exception_table_entry *ex_table_start; • const struct exception_table_entry *ex_table_end; • const struct module_persist *persist_start; • const struct module_persist *persist_end; • int (*can_unload)(void); • };
sys_create_module() • sys_create_module(const char *name_user, size_t size) • 为模块数据结构和模块映象分配核心空间; • 将模块数据结构加入module_list表头;
sys_init_module() • sys_init_module(const char *name_user, struct module *mod_user) • 检查模块映象的合法性; • 更新模块依赖链表;
struct module_ref • struct module_ref { • struct module *dep; • struct module *ref; • struct module_ref *next_ref; • };
sys_delete_module() • sys_delete_module(const char *name_user) • 模块的refs链为空,宏__MOD_IN_USE()返回0,此时可卸载指定模块; • #define __MOD_IN_USE(mod) (mod_member_present((mod), can_unload) && (mod)->can_unload ? (mod)->can_unload() : atomic_read(&(mod)->uc.usecount)) • 更新模块依赖链表和模块链表;
sys_query_module() • sys_query_module(const char name_user, int which, char *buf, size_t bufsize, size_t *ret) • QM_MODULES:返回所有已加载模块名; • QM_DEPS:返回指定模块依赖的所有模块名; • QM_REFS:返回所有引用指定模块的模块名; • QM_SYMBOLS:返回指定模块提供的所有符号名及其地址; • QM_INFO:返回指定模块的地址,大小和状态信息;
sys_get_kernel_syms() • sys_get_kernel_syms(struct kernel_sym *table) • 已过时的系统调用; • 相当于sys_query_module(NULL, QM_SYMBOLS, ...)的功能;
kerneld • 在内核版本1.3.57中引入,存在于版本2.0和2.1中,在2.2及以上版本中被kmod取代; • 分为内核级和用户级两部分; • 内核级:request_module(), release_module(); • 用户级:kerneld守护进程,根据请求执行modprobe,定期卸载空闲模块;
kmod • 是kerneld的替代; • 无须用户级守护进程; • request_module()启动一个核心线程,该线程执行modprobe加载需要的模块; • 不能自动卸载空闲模块;
modutils • ksyms:sys_query_module(…,QM_SYMBOLS,…) • lsmod:sys_query_module(…,QM_MODULES,…) • modinfo:sys_query_module(…,QM_INFO,…) • rmmod:sys_delete_module() • depmod:分析模块树,构造模块依赖文件; • modprobe:根据依赖文件,加载模块栈; • genksyms:构造带版本信息的内核符号; • insmod:构造模块映象并加载;
insmod • 检查版本是否匹配(kernel_version和using_checksums); • 利用内核符号解析模块中的未定义引用; • __this_module; • 处理模块参数; • 构造模块依赖链表;构造__ksymtab,若尚未存在,用模块符号填充; • 重定位; • 构造模块映象,加载;
LKM编程 • Hello World • 版本控制 • 输出符号 • 模块参数 • 调试技术
Hello World • hello.c: • #include <linux/kernel.h> • #include <linux/module.h> • int init_module() { printk(“hello, world!\n”; return 0; } • void cleanup_module() { printk(“bye, world!\n”); } • $ gcc -c -D__KERNEL__ -DMODULE hello.c • 使用多个.c文件: • #define __NO_VERSION__ • $ ld -r -o hello.o init.o clean.o
版本控制 • 最早的版本控制简单的比较内核版本号和模块符号kernel_version的值; • 新的版本控制比较二进制接口的一致性; • 某符号的二进制接口定义为该符号的定义CRC校验和; • 仅当内核和模块都支持时此新方式才起作用,否则沿用旧的版本控制方式; • #define MODVERSIONS • #include <linux/modversions.h>
输出符号 • #define EXPORT_SYMTAB • EXPORT_NO_SYMBOLS; • EXPORT_SYMBOL(name); • EXPORT_SYMBOL_NOVERS(name);
模块参数 • MODULE_PARM(name, type); • type:”min-maxT”; • T:s,c,b,h,i,l
调试技术 • printk • 利用/proc文件系统或ioctl; • ksymoops • 使用调试器 • gdb • kdebug • 远程调试