文档详情

linux内核之旅

第***
实名认证
店铺
DOC
395.50KB
约28页
文档ID:38864214
linux内核之旅_第1页
1/28

内核模块是 Linux 内核向外部提供的一个插口,其全称为动态可加载内核模块 (Loadable Kernel Module,LKM),我们简称为模块模块Linux 内核之所以提供 模块机制,是因为它本身是一个单内核(monolithic kernel)单内核的最大优 点是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性 相对较差,模块机制就是为了弥补这一缺陷 一、 什么是模块 模块是具有独立功能的程序,它可以被单独编译,但不能独立运行它在 运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间 的进程是不同的模块通常由一组函数和数据结构组成,用来实现一种文件系 统、一个驱动程序或其他内核上层的功能 二、 编写一个简单的模块 模块和内核都在内核空间运行,模块编程在一定意义上说就是内核编程 因为内核版本的每次变化,其中的某些函数名也会相应地发生变化,因此模块 编程与内核版本密切相关以下例子针对 2.6 内核1.程序举例hellomod.c 001 // hello world driver for Linux 2.6 004 #include 005 #include 006 #include /* 必要的头文件*/009 static int __init lkp_init( void ) { printk(“Hello,World! from the kernel space…\n”); return 0; 013 }015 static void __exit lkp_cleanup( void ) { printk(“Goodbye, World! leaving kernel space…\n”); 018 } 020 module_init(lkp_init); 021 module_exit(lkp_cleanup); 022 MODULE_LICENSE(“GPL”);.说明 第 4 行:所有模块都要使用头文件 module.h,此文件必须包含进来。

第 5 行:头文件 kernel.h 包含了常用的内核函数 第 6 行:头文件 init.h 包含了宏_init 和_exit,它们允许释放内核占用的内存 建议浏览一下该文件中的代码和注释 第 9-12 行:这是模块的初始化函数,它必需包含诸如要编译的代码、初始化数据 结构等内容 第 11 行使用了 printk()函数,该函数是由内核定义的,功能与 C 库中的 printf() 类似,它把要打印的信息输出到终端或系统日志字符串中的是输出的级 别,表示立即在终端输出 第 15-18 行:这是模块的退出和清理函数此处可以做所有终止该驱动程序时相关 的清理工作 第 20 行:这是驱动程序初始化的入口点对于内置模块,内核在引导时调用该 入口点;对于可加载模块则在该模块插入内核时才调用 第 21 行:对于可加载模块,内核在此处调用 module_cleanup()函数,而对于 内置的模块,它什么都不做 第 22 行:提示可能没有 GNU 公共许可证有几个宏是在 2.4 版的内核中才开发 的(详情参见 modules.h)函数 module_init()和 cleanup_exit()是模块编程中最基本也是必须的两个函数。

module_init()向内核注册模块所提供的新功能,而 cleanup_exit()注销由模块提供 的所有功能模块编程属于内核编程,因此,除了对内核相关知识有所了解外,还需要了解 与模块相关的知识1.应用程序与内核模块的比较 为了加深对内核模块的了解,表一给出应用程序与内核模块程序的比较 表一 应用程序与内核模块程序的比较C 语言应用程序内核模块程序 使用函数Libc 库内核函数 运行空间用户空间内核空间 运行权限普通用户超级用户 入口函数main()module_init() 出口函数exit()module_exit() 编译Gcc –cMakefile 连接Gccinsmod运行直接运行insmod 调试Gdbkdbug, kdb,kgdb 等从表一我们可以看出,内核模块程序不能调用 libc 库中的函数,它运行在内核 空间,且只有超级用户可以对其运行另外,模块程序必须通过 module_init() 和 module-exit()函数来告诉内核“我来了”和“我走了”2.内核符号表(如果对以下第 2~4 点理解上有困难,可以越过)如 前所述,Linux 内核是一个整体结构,像一个圆球,而模块是插入到内核中 的插件。

尽管内核不是一个可安装模块,但为了方便起见,Linux 把内核也看 作 一个“母”模块那么模块与模块之间如何进行交互呢,一种常用的方法就 是共享变量和函数但并不是模块中的每个变量和函数都能被共享,内核只把 各个模块中 主要的变量和函数放在一个特定的区段,这些变量和函数就统称为 符号符号到低哪些符号可以被共享? Linux 内核有自己的规定对于内核这个特 殊的母模块,在 kernel/ksyms.c 中定义了从中可以“移出”的符号,例如进程 管理子系统可以“移出”的符号定义如下:/* 进程管理 */EXPORT_SYMBOL(do_mmap_pgoff);EXPORT_SYMBOL(do_munmap);EXPORT_SYMBOL(do_brk);EXPORT_SYMBOL(exit_mm);…EXPORT_SYMBOL(schedule);EXPORT_SYMBOL(jiffies);EXPORT_SYMBOL(xtime);…你可能对这些变量和函数已经很熟悉其中宏定义 EXPORT_SYMBOL()本身的 含义是“移出符号”为什么说是“移出”呢?因为这些符号本来是内核内部 的符号,通过这个宏放在一个公开的地方,使得装入到内核中的其他模块可以 引用它们。

实际上,仅仅知道这些符号的名字是不够的,还得知道它们在内核地址空间中 的地址才有意义因此,内核中定义了如下结构来描述模块的符号:struct module_symbol{unsigned long value; /*符号在内核地址空间中的地址*/const char *name; /*符号名*/};我们可以从/proc/ksyms 文件中读取所有内核模块“移出”的符号,这所有符 号就形成内核符号表,其格式如下:内存地址 符号名 [所属模块]在模块编程中,可以根据符号名从这个文件中检索出其对应的地址,然后直接 访问该地址从而获得内核数据第三列“所属模块”指符号所在的模块名,对 于从内核这一母模块移出的符号,这一列为空模块加载后,2.4 内核下可通过 /proc/ksyms、 2.6 内核下可通过 /proc/kallsyms 查看模块输出的内核符号3.模块依赖如前所述,内核符号表记录了所有模块可以访问的符号及相应的地址当一个 新的模块被装入内核后,它所申明的某些符号就会被登记到这个表中,而这些 符号可能被其他模块所引用,这就引出了模块依赖这个问题一个模块 A 引用另一个模块 B 所移出的符号,我们就说模块 B 被模块 A 引用, 或者说模块 A 依赖模块 B。

如果要链接模块 A,必须先链接模块 B这种模块间 相互依赖的关系就叫模块依赖模块依赖4.模块引用计数器为 了确保模块安全地卸载,每个模块都有一个引用计数器当执行模块所涉及 的操作时就递增计数器,在操作结束时就递减这个计数器;另外,当模块 B 被 模块 A 引用 时,模块 B 的引用计数就递增,引用结束,计数器递减什么时候 可以卸载这个模块?当然只有这个计数器值为 0 的时候,例如,当一个文件系 统还被安装在系统上 时就不能将其卸载,当这个文件系统不再被使用时,引用 计数器就为 0,于是可以卸载四.模块编译Linux 中最重要的软件开发工具是 GCCGCC 是 GNU 的 C 和 C++ 编译 器但是,在大型的开发项目中,通常有几十到上百个的源文件,如果每次均 手工键入 gcc 命令进行编译的话,则会非常不方便因此,人们通常利用 make 工具来自动完成编译工作利用这种自动编译可大大简化开发工作,避 免不必要的重新编译这些工作包括:如果仅修改了某几个源文件,则只重新 编译这几个源文件;如果某个头文件被修改了,则重新编译所有包含该头文件 的源文件1.编译工具 make实际上,make 工具通过一个称为 Makefile 的文件来完成并自动维护编译工作。

Makefile 需要按照某种语法进行编写,其中说明了如何编译各个源文件并连接 生成可执行文件,并定义了源文件之间的依赖关系下面给出 2.6 内核模块的内核模块的 Makefile 模板(请参看模板(请参看 Makefile 的写法)的写法)# Makefile2.6 obj-m += hellomod.o # 产生 hellomod 模块的目标文件 CURRENT_PATH := $(shell pwd) #模块所在的当前路径 LINUX_KERNEL := $(shell uname -r) #Linux 内核源代码的当前版本 LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL) #Linux 内核源代码的绝对路 径 all: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules #编译模块了 clean: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean #清理注意: 在每个命令前(例如 make 命令前)要键入一个制表符(按 TAB 键产生)有了 Makefile,执行 make 命令,会自动形成相关的后缀为.o 和.ko 文件。

到此,模块编译好了,该把它插入到内核了: 如:$insmod hellomod.ko当然,要以系统员的身份才能把模块插入成功插入后,可以通过 dmesg 命令查看,屏幕最后几行的输出就是你程序中 输出的内容:Hello,World! from the kernel space…当模块不再需要时,可以通过 rmmod 命令移去,例如$rmmod hellomodTags: Add new tagmodutils 是管理内核模块的一个软件包可以在任何获得内核源代码的地方获取Modutils(modutils-x.y.z.tar.gz)源代码,然后选择最高级别的 patch.x.y.z 等于或小于当前的内 核版本,安装后在/sbin 目录下就会有 insomod、rmmod、ksyms、lsmod、modprobe 等实用 程序当然,通常我们在加载 Linux 内核时,modutils 已经被载入 1.Insmod 命令 调用 insmod 程序把需要插入的模块以目标代码的形式插入到内核中在插入的时候, insmod 自动调用 init_module()函数运行注意,只有超级用户才能使用这个命令,其命令 格式为: # insmod [path] modulename.c 2. rmmod 命令 调用 rmmod 程序将已经插入内核的模块从内核中移出,rmmod 会自动运行 cleanup_module()函数,其命令格式为: #rmmod [path] modulename.c 3.lsmod 命令 调用 lsmod 程序将显示当前系统中正在使用的模块信息。

实际上这个程序的功能就是读取 /proc 文件系统中的文件/proc/modules 中的信息,其命令格式为: #lsmod 4.ksyms 命令 ksyms 这个程序用来显示内核符号和模块符号表的信息与 lsmod 相似,它的功能是读取 /proc 文件系统中的另一个文件/proc/kallsyms在此,我们将编写一个模。

下载提示
相似文档
正为您匹配相似的精品文档