版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux内核模块编程主要参考书目《Linux设备驱动开发详解(第2版)》宋宝华编著人民邮电出版社主要参考书目《Linux设备驱动程序(第三版)》魏永明耿岳等译中国电力出版社主要参考书目《精通Linux驱动程序开发》Sreekrishnan编著人民邮电出版社操作系统主要功能:CPU管理存储管理设备管理文件管理网络与通信管理用户接口POSIX表示可移植操作系统接口(PortableOperatingSystemInterface)是为解决应用程序平台移植性提出的一种标准。Linux是什么?Linux是一个由几百万行源代码组成的庞大、复杂的程序,任何人都能从上下载。Linux是一套免费的、源代码开放的、符合POSIX标准标准的操作系统。严格来说,Linux只包含以下图中内核与系统调用接口那两层。Linux内核的构成8Linux的虚拟内存管理Linux的虚拟内存管理机制为应用程序和驱动程序提供了两种效劳:使每个进程都拥有自己独立的内存地址空间;对于32位Linux而言,每个任务可寻址的内存地址空间都为0x00000000~0xFFFFFFFF(232,4GB)当物理内存不够4GB时,虚拟内存管理模块会用外存空间模拟内存空间,并且该模拟过程对应用程序是透明的。9用户地址空间与内核地址空间Linux将每个进程的4GB的独立地址空间又划分为用户地址空间(0x00000000~0xBFFFFFFF)和内核地址空间(0xC0000000~0xFFFFFFFF)两局部。操作系统内核代码和数据存放在内核地址空间;每个进程自己私有的代码和数据存放在用户地址空间虽然Linux的内核代码和数据被映射到了每个进程的地址空间中〔所有进程看到的内容是相同的〕,但在实际的物理内存中,只有内核代码和数据的一份拷贝。10用户地址空间与内核地址空间虚拟页物理页031null213null虚拟页物理页021null213null虚拟页物理页0null1null213null物理页taken进程id0Nnull1Yos2Y23Y1进程1的页表进程id虚拟页硬盘中的地址进程2的页表进程3的页表内页表外页表CR3寄存器CPU负责查表(虚拟地址->物理地址),查表失败时触发缺页中断(14号);OS负责填充各个表的内容,并提供缺页中断的中断效劳器程序。用户态与核心态一般现代CPU都有几种不同的指令执行级别在高执行级别下,代码可以执行特权指令,访问任意的物理地址,这种CPU执行级别就对应着内核态用户态指相应的低级别执行状态,代码的掌控范围会受到限制,只能执行CPU指令集的一个子集举例:intelx86CPU有四种不同的执行级别0-3,Linux只使用了其中的0级和3级分别来表示内核态和用户态0xc0000000以上的内核地址空间只能在内核态下访问,0x00000000-0xbfffffff的用户地址空间在两种状态下都可以访问应用程序可以通过Linux系统调用由用户态进入内核态Linux进程状态Linux中任务和进程是相同的术语,每个进程由task_struct结构来描述,即PCB〔进程控制块〕Linux将进程状态主要分为五种:TASK_RUNNINGTASK_INTERRUPTIBLETASK_UNINTERRUPTIBLETASK_STOPPEDTASK_ZOMBILE。进程的状态随着进程的调度发生改变Linux进程状态转换可装载内核模块为了使系统功能能够更灵活的扩充,Linux支持内核的动态扩展,即在系统运行时给内核增加新的功能〔即模块module〕。模块〔module〕是一段可以被动态链接的目标代码〔.ko〕,它可由insmod命令动态的装载并链接到正在运行的内核。链接后,它就成了内核的一局部,直到用rmmod命令解除链接并卸载。Linux驱动程序就是一种特殊的内核模块。内核模块与应用程序的不同内核模块工作在内核空间〔supervisorspace〕,而应用程序工作在用户空间〔userspace〕内核模块是一个由多个回调函数组成的“被动”代码集合体,采用了“事件驱动模型”;而应用程序总是从头至尾的执行单个任务。内核模块不能调用C标准函数库〔glibc库〕,只能调用linux内核导出的内核函数。内核模块在编程时必须考虑可重入性〔reentrant〕内核模块可使用的栈很小(一般只有4096字节)。内核模块程序源码的构成头文件
#include<linux/init.h>#include<linux/module.h>必选许可声明MODULE_LICENSE("DualBSD/GPL");必选加载函数staticint__inithello_init(void)必选卸载函数staticvoid__exithello_exit(void)必选模块参数module_param(num,int,S_IRUGO)可选模块导出符号EXPORT_SYMBOL(add_integer)可选模块作者等信息声明MODULE_AUTHOR(“author_name”)可选最简单的KernelModule#include<linux/init.h>#include<linux/module.h>MODULE_LICENSE("DualBSD/GPL");staticint__inithello_init(void){//这是模块加载函数printk(KERN_ALERT"Hello,world\n");return0;}staticvoid__exithello_exit(void){//这是模块卸载函数printk(KERN_ALERT"Goodbye,cruelworld\n");}module_init(hello_init);module_exit(hello_exit);模块加载函数通过insmod或modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成模块的相关初始化工作,通常包括:(1)向内核注册一些数据结构;〔2〕申请软硬件资源;〔3〕初始化硬件模块加载函数必须用宏“module_init”指定,它返回整型值。假设初始化成功那么返回0,假设失败那么返回一个负值作为错误码。“__init”和“__exit”都是宏,利用了gcc的扩展关键字,分别要求编译器将所声明函数的目标代码放入“init.text”段和“exit.text”段中(两个特殊的ELF段)。static关键字为了将该函数名的可见性控制在本文件内。#define__init__attribute__((__section__(“.init.text”)))#define__exit__attribute__((__section__(“.exit.text”)))module_init背后的秘密#definemodule_init(x)__initcall(x);#define__initcall(fn)device_initcall(fn)#definedevice_initcall(fn)__define_initcall("6",fn,6)#define__define_initcall(level,fn,id)\staticinitcall_t__initcall_##fn##id__used\__attribute__((__section__(".initcall"level".init")))=fn所以,module_init(x)最终展开为:staticinitcall_t__initcall_##fn##id__used\__attribute__((__section__(".initcall"level".init")))=fninitcall_t是一个指向函数的指针类型typedefint(*initcall_t)(void)所以module_init本质上是将一个函数指针变量放在了一名为".initcall6.init"ELF节中。ExecutableandLinkingFormat模块卸载函数模块卸载函数必须用宏“module_exit”指定,无返回值。当通过rmmod命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块装载函数相反的功能。〔注销一些内核数据结构,释放资源等〕。内核打印函数printkprintk〔fmt,args…)级别KERN_EMERG用于紧急消息,常常是那些崩溃前的消息.KERN_ALERT需要立刻动作的情形.KERN_CRIT严重情况,常常与严重的硬件或者软件失效有关.KERN_ERR用来报告错误情况;设备驱动常常使用KERN_ERR来报告硬件故障.KERN_WARNING有问题的情况的警告,这些情况自己不会引起系统的严重问题.KERN_NOTICE正常情况,但是仍然值得注意.在这个级别一些平安相关的情况会报告.KERN_INFO信息型消息.在这个级别,很多驱动在启动时打印它们发现的硬件的信息.KERN_DEBUG用作调试消息.不能打印浮点数printk消息流向/etc/syslog.conf中可配置syslogd的分发规那么,例如可以参加: kern.*/tmp/kernel_debug.txt/proc/kernel/printk文件中设置了一个优先级,高于该优先级的消息才能显示到控制台中模块许可证声明模块许可证声〔MODULE_LICENSE〕明描述内核模块的许可权限如果不声明LICENSE,模块被加载时,将收到内核被污染〔kerneltainted〕的警告编译内核模块的条件已安装了GCC工具链有一份内核源码,且至少被编译过一次。内核模块程序在编译过程中要使用内核源码的头文件〔在include目录〕和编译内核时生成的符号文件。内核模块的编译可以编写一个最简单的Makefile:obj-m:=hello.o并采用如下命令编译:make–C/lib/modules/$(shelluname-r)/buildM=$(shellpwd)modules或采用如下较复杂的Makefile:ifneq($(KERNELRELEASE),)#callfromkernelbuildsystem obj-m :=hello.oelse KERNELDIR:=/lib/modules/$(shelluname-r)/build PWD:=$(shellpwd)default: make-C$(KERNELDIR)M=$(PWD)modulesendif内核模块的编译如果我们想由两个源文件(比方file1.c和file2.c)构造出一个名称为module.ko的模块,那么makefile的obj-m变量可如下编写:obj-m:=module.omodule-objs:=file1.ofile2.o内核模块的加载与卸载使用insmod命令或modprobe加载模块:insmod./hello.ko使用rmmod命令卸载模块:rmmodhello使用lsmod命令查看内核中已加载的内核模块的信息通过查看/proc/modules文件也可查看内核中已加载的内核模块的信息。通过查看/sys/module目录也可查看内核中已加载的内核模块的信息。modprobe会考虑要装载的模块是否引用了一些当前内核不存在的符号。如果有这类引用,modprobe会在当前模块路径中搜索定义了这些符号的其他模块,并同时将这些模块也装载到内核。(/lib/modulesmodules.dep/ect/modprob.conf)内核模块参数module_param〔参数名,参数类型,参数读/写权限〕内核支持的模块参数类型包括:byte、short、ushort、int、uint、long、ulong、charp(字符指针)、bool。staticchar*whom="world";staticinthowmany=1;module_param(howmany,int,S_IRUGO);module_param(whom,charp,S_IRUGO);装载模块指定参数可通过insmod或modprobe在装载模块时为其传入参数:
insmodhello_ext.kohowmany=5whom="Students"modprobe也可以从它的配置文件(/etc/modprobe.conf)读取参数的值最简单的KernelModule…………//export.c
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论