1、Linux操作系统分析与实践 实验二:内存管理,Linux操作系统分析与实践课程建设小组 北京大学 二零零八年春季*致谢:感谢Intel对本课程项目的资助,实验目的,1理解Linux虚拟内存管理的机制。 2学习模块编程的基本技能。,实验内容,1模块 编写一个内核模块,分别实现如下几个函数: static void mtest_dump_vma_list(void):依次列出当前进程各段的读、写或执行权限。 static void mtest_find_vma(unsigned long addr):找到虚地址addr所在的vma,通过printk打印该段的起始地址、终止地址、段标志等信息。 s
2、tatic void mtest_find_page(unsigned long addr):找到虚拟地址addr,显示出其对应的物理地址。为了测试和使用以上函数,在内核模块的初始化函数中创建一个名为mtest的/proc文件。mtest文件绑定的写函数mtest_write允许用户程序写入一串字符串,指定要使用以上的那个内核函数及其参数。mtest_write函数的定义为: static ssize_t mtest_write(struct file *file, const char _user * buffer,size_t count, loff_t * data),2测试程序 为了测
3、试以上模块的正确性,要求编写一个称为test的应用程序,它通过打开/proc文件mtest,调用模块内的相关函数并给出相应的测试信息。,Linux内核模块,Linux操作系统的内核是单一体系结构(monolithic kernel) 有了模块机制后,提高Linux操作系统的可扩充性,内核编程不再是一个恶梦 什么是模块呢? 模块的全称是“动态可加载内核模块”(Loadable Kernel Module,LKM) 模块在内核空间运行 模块实际上是一种目标对象文件 没有链接,不能独立运行,但是其代码可以在运行时链接到系统中作为内核的一部分运行或从内核中取下,从而可以动态扩充内核的功能 这种目标代码
4、通常由一组函数和数据结构组成,Linux内核模块的优点与缺点,优点 使得内核更加紧凑和灵活 修改内核时,不必全部重新编译整个内核。系统如果需要使用新模块,只要编译相应的模块,然后使用insmod将模块装载即可 模块的目标代码一旦被链接到内核,它的作用域和静态链接的内核目标代码完全等价 缺点 由于内核所占用的内存是不会被换出的,所以链接进内核的模块会给整个系统带来一定的性能和内存利用方面的损失; 装入内核的模块就成为内核的一部分,可以修改内核中的其他部分,因此,模块的使用不当会导致系统崩溃; 为了让内核模块能访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改符号表; 模块会要求利用其
5、它模块的功能,所以,内核要维护模块之间的依赖性.,Linux内核模块与应用程序的区别,C语言程序 Linux内核模块运行 用户空间 内核空间 入口 main() module_init()指定; 出口 无 module_exit()指定; 编译 gcc c Makefile 连接 ld insmod 运行 直接运行 insmod 调试 gdb kdbug, kdb, kgdb等,模块相关命令,insmod module parameters Load the module 注意,只有超级用户才能使用这个命令 Rmmod Unload the module lsmod List all modu
6、les loaded into the kernel 这个命令和cat /proc/modules等价 modprobe -r Load the module specified and modules it depends,模块依赖,一个模块A引用另一个模块B所导出的符号,我们就说模块B被模块A引用。 如果要装载模块A,必须先要装载模块B。否则,模块B所导出的那些符号的引用就不可能被链接到模块A中。这种模块间的相互关系就叫做模块依赖。,最简单的内核模块例子,#include #include #include static int _init hello_init(void) printk(
7、KERN_INFO “Hello worldn“);return 0; static void _exit hello_exit(void) printk(KERN_INFO “Goodbye worldn“); module_init(hello_init); module_exit(hello_exit);,static int _init hello_init(void) static void _exit hello_exit(void) Static声明,因为这种函数在特定文件之外没有其它意义 _init标记, 该函数只在初始化期间使用。模块装载后,将该函数占用的内存空间释放 _ex
8、it标记 该代码仅用于模块卸载。 Init/exit 宏:module_init/module_exit 声明模块初始化及清除函数所在的位置 装载和卸载模块时,内核可以自动找到相应的函数module_init(hello_init);module_exit(hello_exit);,编译内核模块,Makefile文件 obj-m := hello.o all: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules clean: make -C /lib/modules/$(shell uname -r)/buil
9、d M=$(shell pwd) clean Module includes more files obj-m:=hello.o hello-objs := a.o b.o,装载和卸载模块,相关命令 lsmod insmod hello.ko rmmod hello.ko,模块参数传递,有些模块需要传递一些参数参数在模块加载时传递#insmod hello.ko test=2参数需要使用module_param宏来声明module_param的参数:变量名称,类型以及访问许可掩码支持的参数类型 Byte, short, ushort, int, uint, long, ulong, bool,
10、 charp Array (module_param_array(name, type, nump, perm),#include #include #include #include static int test; module_param(test, int, 0644);static int _init hello_init(void) printk(KERN_INFO “Hello world test=%d n” , test);return 0; static void _exit hello_exit(void) printk(KERN_INFO “Goodbye worldn
11、“); MODULE_LICENSE(“GPL“); MODULE_DESCRIPTION(“Test“); MODULE_AUTHOR(“xxx“); module_init(hello_init); module_exit(hello_exit);,运行结果,导出符号表,如果一个模块需要向其他模块导出符号(方法或全局变量),需要使用:EXPORT_SYMBOL(name);EXPORT_SYMBOL_GPL(name);*注意:符号必须在模块文件的全局部分导出,不能在函数部分导出。更多信息可参考 文件 Modules仅可以使用由Kernel或者其他Modules导出的符号不能使用Libc
12、/proc/kallsyms 可以显示所有导出的符号,内核模块操作/proc文件,/proc文件系统,这是内核模块和系统交互的两种主要方式之一。 /proc文件系统也是Linux操作系统的特色之一。 /proc文件系统不是普通意义上的文件系统,它是一个伪文件系统。 通过/proc,可以用标准Unix系统调用(比如open()、read()、write()、 ioctl()等等)访问进程地址空间 可以用cat、more等命令查看/proc文件中的信息。 用户和应用程序可以通过/proc得到系统的信息,并可以改变内核的某些参数。 当调试程序或者试图获取指定进程状态的时候,/proc文件系统将是你强有力的支持者。通过它可以创建更强大的工具,获取更多信息。,Q&A,开始实验 !参考实验指导书完成本次实验,