1 / 30

Linux 模块编程

Linux 模块编程. 主要内容. 1. 2. Linux 内核模块机制介绍. 内核编程. Linux 内核模块机制介绍. 1. 微内核与单内核 Linux 的模块机制. 先来区分进程的两种运行模式: 用户模式 (user mode) 运行在用户空间,实现用户的功能 内核模式 (kernel mode 运行在内核空间,一般是实现系统相关的功能。 进程在自己空间中实现用户功能时,如果想使用系统功能,必须将运行级别从用户态提升到核心态才能访问内核空间,实现相关操作。这个过程是通过系统调用实现的, 而用户态切换到核心态过程不可避免的存在一定开销 。.

patty
Download Presentation

Linux 模块编程

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Linux模块编程

  2. 主要内容 1 2 Linux内核模块机制介绍 内核编程

  3. Linux内核模块机制介绍 1 微内核与单内核 Linux的模块机制

  4. 先来区分进程的两种运行模式: • 用户模式(user mode) 运行在用户空间,实现用户的功能 • 内核模式(kernel mode 运行在内核空间,一般是实现系统相关的功能。 进程在自己空间中实现用户功能时,如果想使用系统功能,必须将运行级别从用户态提升到核心态才能访问内核空间,实现相关操作。这个过程是通过系统调用实现的,而用户态切换到核心态过程不可避免的存在一定开销。

  5. 1.两种体系结构下的内核 • 微内核(Micro kernel) 最常用的功能模块被设计成内核模式运行的一个或一组进程,通常只包含进程调度、内存管理和进程间通信几个基本功能。其他功能都作为单独的进程在用户模式下运行,通过信号量、邮箱等信息传递方式进行通信。 • 灵活、 易于移植。 • 进程间通信开销大,速度相对较慢。 • 单内核(Monolithic kernel,有时也叫宏内核Macro kernel) 内核一般做为一个大进程存在,内部各功能模块可直接调用彼此的函数。 • 因为都在内核空间,没有切换开销,效率上有一定优势。 • 可扩展性和可维护性比较差。

  6. Linux操作系统内核是单内核,速度和性能都很高,问题在于如何提高可扩展性和可维护性? Linux操作系统内核是单内核,速度和性能都很高,问题在于如何提高可扩展性和可维护性? 模块(Module)机制 用户可以根据需要,在不需要对内核重新编译的情况下,可以将模块动态地载入或移出内核。 2.Linux的模块机制

  7. 模块编程 2 模块的编写 模块的编译 模块的安装与使用

  8. 内核模式下编程的一些限制 : • 不能使用用户模式下的C标准库,因为内核模式下不存在libc库,也就没有这些用户函数供调用。 • 不能使用浮点运算,因为linux内核切换模式时不保存处理器的浮点状态。 • 不要让内核程序进行长时间等待,因为linux内核是非抢占的。 • 尽可能保持代码的清洁易懂,因为内核调试不方便,简洁的代码能减少并方便后期调试。

  9. 一个模块一般包含如下几部分 许可证声明 模块初始化和退出函数声明 初始化和退出函数 普通函数 模块导出符号表 其他操作 1.模块的编写

  10. 在C语言中,关键字static有三个明显的作用: • 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 • 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。 • 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。 

  11. 关于模块导出符号表 只有模块中导出的内核函数才可以被其它模块调用。导出的内核符号表被看作是导出的内核接口,也可以看作内核API。 在内核中,导出内核函数使用的指令有:

  12. 关于模块的许可证 从Linux内核2.4.10开始,动态加载的模块必须通过MODULE_LICENSE宏声明此模块的许可证,否则动态加载时,会收到内核被污染“module license ‘unspecified’ taints kernel.”的警告。 被内核接受的许可证有很多,最常用的的是“GPL”和"Dual BSD/GPL"。 书写格式如下: • MODULE_LICENSE("GPL"); • MODULE_LICENSE("Dual BSD/GPL");

  13. Linux操作系统内核模块也可带参数 定义一个模块参数 module_param(name, type, perm); name 参数名 type 参数类型 perm 指定模块在sysfs文件系统下的对应文件权限,可用八进制,或S_Ifoo形式,如S_IRUGO|S_IWUSR表示任何人可读,所有者可写。 如:static char *user_name=“username”; module_param(user_name,charp,S_IRUGO); 安装模块时使用参数 insmod module.ko user_name=book_user1 type参数: byte字节 short短整型 ushort无符号短整型 int整型 uint无符号整型 long长整型 ulong无符号长整型 charp字符指针 bool布尔类型 关于模块的参数

  14. 关于模块使用计数 内核需要记录加载到系统里的每一个模块的使用情况。 • 在Linux操作系统2.4内核中使用两个宏来完成对模块引用计数的操作: • 在Linux操作系统2.6内核中,使用下面的两个函数来完成对模块引用计数的操作:

  15. 内核编写实例 编写一个内核模块module,向外导出两个函数,分别是“求累积和”和“求阶乘”功能。 编写另两个内核模块module1、module2,分别使用上面module模块中的函数,实现计算。 • 注意路径清晰,分别在三个目录下编写

  16. 内核调试常用函数——printk printk是内核使用的函数,接口和printf()基本相似,可以在控制台显示多达1024个字符。 内核根据“记录级别”判断是否在终端打印消息。没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL(级别在4),看不到输出,因为正常输出的前提是──日志输出级别小于console_loglevel(在内核中数字越小优先级越高)。这些级别都是简单宏定义,可在include / linux / kernel.h中查看。

  17. #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int __init mod_init_modtest(void); static void __exit mod_exit_modtest(void); module_init(mod_init_modtest); module_exit(mod_exit_modtest); int sum_op(int numdata); int factorial_op(int N); //初始化与退出函数 int mod_init_modtest(void) { printk(KERN_INFO"--------Module_export_symbol init !---------\n"); return 0; } void mod_exit_modtest(void) { printk(KERN_INFO"-----Module_export_symbol was deleted!-----\n"); } //普通函数=== EXPORT_SYMBOL(sum_op); EXPORT_SYMBOL(factorial_op); MODULE_AUTHOR("book author"); MODULE_DESCRIPTION("module1:Module_export_symbol --sum_op--factorial_op--"); MODULE_VERSION("Ver 1.0"); int sum_op(int numdata) //用于计算比某一数字小的所有正整数的和, { char i = 0; char ret = 0; printk(KERN_INFO"sum operation\n"); while(i <= numdata) ret += i++; return ret; } int factorial_op(int N) //factorial_op( ) 用于计算某一数字的阶乘 { char i=1; int Nx=1; printk(KERN_INFO"factorial operation\n"); if( N == 0) return Nx; for(;i<=N;i++) Nx=Nx*i; return Nx; } module.c

  18. #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int __init mod_init_modtest1(void); static void __exit mod_exit_modtest1(void); module_init(mod_init_modtest1); module_exit(mod_exit_modtest1); static char *user_name = "book_user"; static int num_operator = 0; extern int sum_op(int); int mod_init_modtest1(void) { int result = 0; printk(KERN_INFO"Hello,I am module 1 !\n"); printk(KERN_INFO"%s,Welcome to use this sum_op!\n",user_name); result = sum_op(num_operator); printk(KERN_INFO"1 +..+ %d = %d\n",num_operator,result); return 0; } void mod_exit_modtest1(void) { printk(KERN_INFO"Module 1 : Goodbye %s\n",user_name); } module_param(user_name,charp,S_IRUGO); module_param(num_operator,int,S_IRUGO); MODULE_AUTHOR("book author"); MODULE_DESCRIPTION("Simple Module 1 ,used to sum_op"); MODULE_VERSION("Ver 1.0"); 声明为内核模块的参数 module1.c 安装模块时不提供参数时,缺省的参数值 改成KERN_ALERT级别试试。 module1用到了module中的导出符号,其makefile中会有一句: KBUILD_EXTRA_SYMBOLS=../module/Module.symvers

  19. #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int __init mod_init_modtest2(void); static void __exit mod_exit_modtest2(void); module_init(mod_init_modtest2); module_exit(mod_exit_modtest2); static char *user_name = "book_user"; static int num_operator = 0; extern int factorial_op(int); int mod_init_modtest2(void) { int result = 0; printk(KERN_INFO"Hello,I am module 2 !\n"); printk(KERN_INFO"%s,Welcome to use this factorial_op!\n",user_name); result = factorial_op(num_operator); printk(KERN_INFO" %d! = %d\n",num_operator,result); return 0; } void mod_exit_modtest2(void) { printk(KERN_INFO"Module 2 : Goodbye %s\n",user_name); } module_param(user_name,charp,S_IRUGO); module_param(num_operator,int,S_IRUGO); MODULE_AUTHOR("book author"); MODULE_DESCRIPTION("Simple Module 2 ,used to factorial_op"); MODULE_VERSION("Ver 1.0"); 与上一模块不同点只是调用的函数不一样 module2.c

  20. 分别执行make命令,编译module.c、module1.c、module2.c分别执行make命令,编译module.c、module1.c、module2.c

  21. 安装模块

  22. 查看模块lsmod 查看安装是否成功,下图中可看到module1和module2的使用者数量为0,而module被module1和module2使用,因此其使用者数量为2。

  23. 查看模块输出信息 利用dmesg 命令获得一个内核日志的副本,查看系统信息:dmesg | tail -10

  24. 不同版本模块编译问题 问题:Linux kernel2.6.26版本及以后内核版本编译生成module1.ko时有警告信息,模块安装时会提示符号sum_op(定义在module中)对于模块module1来说不可见。 解决办法:把生成module.ko模块时生成的Module.symvers放到module1文件夹中。这样编译module1时,符号信息就会自动链接进去了。

  25. 2.模块的编译 经过编译、链接后生成的内核模块文件的后缀为.ko。 内核的makefile系统共有5种类型的文件,从linux2.6内核开始,模块的编译需要配置过的内核源码。

  26. Linux内核Makefile分类 • Kernel Makefile Kernel Makefile位于Linux内核源代码的顶层目录,也叫 Top Makefile。它主要用于指定编译Linux Kernel目标文件(vmlinux)和模块(module)。编译内核或模块时,这个文件会被首先读取,并根据读到的内容配置编译环境变量。对于内核或驱动开发人员来说,这个文件几乎不用任何修改。 • ARCH Makefile ARCH Makefile位于ARCH/$(ARCH)/Makefile,是系统对应平台的Makefile。Kernel Top Makefile会包含这个文件来指定平台相关信息。只有平台开发人员会关心这个文件。 • Kbuild Makefile Kbuild系统使用Kbuild Makefile来编译内核或模块。当Kernel Makefile被解析完成后,Kbuild会读取相关的Kbuild Makefile进行内核或模块的编译。Kbuild Makefile有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild Makefile文件。

  27. Kbuild Makefile模板语法说明 内核源码顶层makefile中定义的变量 ifneq ($(KERNELRELEASE),) module-objs=module*.o obj-m += module1.o else PWD := $(shell pwd) KVER := $(shell uname -r) KDIR := /lib/modules/$(KVER)/build all: $(MAKE) -C $(KDIR) M=$(PWD) clean: rm -rf *.o *.mod.c *.ko *.symvers *.order *.markers endif • 模块名-objs=依赖关系 • obj-m += 模块编译链接生成的目标文件名。obj-m指明是动态编译 当前目录 顶层makefile所在目录 内核版本 • -C $(a) 指明跳转到a目录下读那里的makefile • M=$(b) 指明返回b目录下处理那里的makefile • 这样写主要是为了在make当前目录下的makefile前先读顶层makefile中的一些变量(如本例中使用的KERNELRELEASE)。

  28. 先读顶层makefile的举例: obj-$(CONFIG_EXT2_FS) += ext2.o ext2-y := balloc.o bitmap.o ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o 设模块名为ext2。将上述行写在ifneq下,意思是: • balloc.o和bitmap.o两个目标文件最终链接生成ext2.o; • ext2.ko是否包括xattr.o取决于内核配置文件中配置的CONFIG_EXT2_FS。 • 需要注意该kbuild Makefile所在的目录中不应该再包含和模块名相同的源文件如ext2.c/ext2.s。

  29. 3.模块的安装与使用 注意模块操作都在root权限下进行 1)模块安装(insmod) insmod 模块名.ko 注意有参数的模块别忘记安装时提供参数。 2)查看已安装模块(lsmod) 3)查看模块运行提示信息(dmesg) 4)模块退出(rmmod) rmmod 模块名 模块使用者计数为0时才能退出。

  30. 思考与练习 1.内核编程会受到哪些限制? 2.Linux内核编程的模块机制带来哪些好处? 3.分析模块编译的makefile文件,说明模块的编译过程。 4.编写三个模块文件mainmod.c、lenmod.c、summod.c,实现对某一数组的求和:在mainmod模块调用summod模块对数组进行求和运算,summod模块调用lenmod模块求数组中元素的个数。 5.说明查看系统信息的方法有哪几种?

More Related