您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 公司方案 > Linux操作系统分析与实践
Linux操作系统分析与实践第七讲:Linux驱动程序编写基础《Linux操作系统分析与实践》课程建设小组北京大学二零零八年春季*致谢:感谢Intel对本课程项目的资助本讲主要内容•Linux内核模块•中断和中断处理•下半部Linux内核模块•Linux操作系统的内核是单一体系结构(monolithickernel)•有了模块机制后,提高Linux操作系统的可扩充性,内核编程不再是一个恶梦•什么是模块呢?•模块的全称是“动态可加载内核模块”(LoadableKernelModule,LKM)•模块在内核空间运行•模块实际上是一种目标对象文件•没有链接,不能独立运行,但是其代码可以在运行时链接到系统中作为内核的一部分运行或从内核中取下,从而可以动态扩充内核的功能•这种目标代码通常由一组函数和数据结构组成①Linux内核模块的优点与缺点•优点–使得内核更加紧凑和灵活–修改内核时,不必全部重新编译整个内核。系统如果需要使用新模块,只要编译相应的模块,然后使用insmod将模块装载即可–模块的目标代码一旦被链接到内核,它的作用域和静态链接的内核目标代码完全等价•缺点–由于内核所占用的内存是不会被换出的,所以链接进内核的模块会给整个系统带来一定的性能和内存利用方面的损失;–装入内核的模块就成为内核的一部分,可以修改内核中的其他部分,因此,模块的使用不当会导致系统崩溃;–为了让内核模块能访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改符号表;–模块会要求利用其它模块的功能,所以,内核要维护模块之间的依赖性.②Linux内核模块与应用程序的区别C语言程序Linux内核模块运行用户空间内核空间入口main()module_init()指定;出口无module_exit()指定;编译gcc–cMakefile连接ldinsmod运行直接运行insmod调试gdbkdbug,kdb,kgdb等③模块相关命令•insmodmodule.ko[moduleparameters]–Loadthemodule–注意,只有超级用户才能使用这个命令•Rmmod–Unloadthemodule•lsmod–Listallmodulesloadedintothekernel–这个命令和cat/proc/modules等价•modprobe[-r]modulename––Loadthemodulespecifiedandmodulesitdepends④模块依赖•一个模块A引用另一个模块B所导出的符号,我们就说模块B被模块A引用。•如果要装载模块A,必须先要装载模块B。否则,模块B所导出的那些符号的引用就不可能被链接到模块A中。这种模块间的相互关系就叫做模块依赖。⑤最简单的内核模块例子#includelinux/kernel.h#includelinux/module.h#includelinux/init.hstaticint__inithello_init(void){printk(KERN_INFOHelloworld\n);return0;}staticvoid__exithello_exit(void){printk(KERN_INFOGoodbyeworld\n);}module_init(hello_init);module_exit(hello_exit);•staticint__inithello_init(void)•staticvoid__exithello_exit(void)–Static声明,因为这种函数在特定文件之外没有其它意义–__init标记,该函数只在初始化期间使用。模块装载后,将该函数占用的内存空间释放–__exit标记该代码仅用于模块卸载。•Init/exit–宏:module_init/module_exit–声明模块初始化及清除函数所在的位置–装载和卸载模块时,内核可以自动找到相应的函数module_init(hello_init);module_exit(hello_exit);⑥编译内核模块•Makefile文件obj-m:=hello.oall:make-C/lib/modules/$(shelluname-r)/buildM=$(shellpwd)modulesclean:make-C/lib/modules/$(shelluname-r)/buildM=$(shellpwd)clean•Moduleincludesmorefilesobj-m:=hello.ohello-objs:=a.ob.o⑦装载和卸载模块•相关命令–lsmod–insmodhello.ko–rmmodhello.ko⑧模块参数传递•有些模块需要传递一些参数•参数在模块加载时传递#insmodhello.kotest=2•参数需要使用module_param宏来声明module_param的参数:变量名称,类型以及访问许可掩码•支持的参数类型Byte,short,ushort,int,uint,long,ulong,bool,charpArray(module_param_array(name,type,nump,perm))#includelinux/kernel.h#includelinux/module.h#includelinux/init.h#includelinux/moduleparam.hstaticinttest;module_param(test,int,0644);staticint__inithello_init(void){printk(KERN_INFO“Helloworldtest=%d\n”,test);return0;}staticvoid__exithello_exit(void){printk(KERN_INFOGoodbyeworld\n);}MODULE_LICENSE(GPL);MODULE_DESCRIPTION(Test);MODULE_AUTHOR(xxx);module_init(hello_init);module_exit(hello_exit);导出符号表•如果一个模块需要向其他模块导出符号(方法或全局变量),需要使用:EXPORT_SYMBOL(name);EXPORT_SYMBOL_GPL(name);*注意:符号必须在模块文件的全局部分导出,不能在函数部分导出。更多信息可参考linux/module.h文件•Modules仅可以使用由Kernel或者其他Modules导出的符号不能使用Libc•/proc/kallsyms可以显示所有导出的符号⑨内核模块操作/proc文件•/proc文件系统,这是内核模块和系统交互的两种主要方式之一。•/proc文件系统也是Linux操作系统的特色之一。•/proc文件系统不是普通意义上的文件系统,它是一个伪文件系统。•通过/proc,可以用标准Unix系统调用(比如open()、read()、write()、ioctl()等等)访问进程地址空间•可以用cat、more等命令查看/proc文件中的信息。•用户和应用程序可以通过/proc得到系统的信息,并可以改变内核的某些参数。•当调试程序或者试图获取指定进程状态的时候,/proc文件系统将是你强有力的支持者。通过它可以创建更强大的工具,获取更多信息。/proc相关函数•create_proc_entry()创建一个文件•proc_symlink()创建符号链接•proc_mknod()创建设备文件•proc_mkdir()创建目录•remove_proc_entry()删除文件或目录⑩Linux2.6内核中有关模块部分的改变•模块引用计数器Linux2.4中在linux/module.h中定义了三个宏来维护实用计数:__MOD_INC_USE_COUNT当前模块计数加一__MOD_DEC_USE_COUNT当前模块计数减一__MOD_IN_USE计数非0时返回真在Linux2.6中,模块引用计数器由系统自动维护,所以程序中有关这些宏都可以注释掉。•关于符号导出列表(listofexportedsymbols)Linux2.4中会用EXPORT_NO_SYMBOLS宏,来表示不想导出任何变量或函数。在Linux2.6中这个宏也已经消失。系统默认为不导出任何变量或函数。•模块程序编译方法的改变Linux2.4中命令为:gcc–Wall–DMODULE–D__KERNEL__-DLINUX–c源文件名.c其中:__KERNEL__:即告诉头文件这些代码将在内核模式下运行MODULE:即告诉头文件要给出适当的内核模块的定义LINUX:并非必要-Wall:显示所有warning信息。•Linux2.6中必须写makefile。通过make命令编译程序。Linux2.6中makefile的写法:(以helloworld为例)//Makefileobj-m+=hello.oall:make-C/lib/modules/$(shelluname-r)/buildM=$(PWD)modulesclean:make-C/lib/modules/$(shelluname-r)/buildM=$(PWD)clean*注意:all下一行需要有一个“Tab”键,不要写成空格或略去,不然系统无法识别,报错:nothingtobedonefor“all”。•用以加载的目标文件类型已经改变–Linux2.4中用以加载为模块的目标文件扩展名为.o–Linux2.6中中用以加载为模块的目标文件扩展名为.ko•在Linux2.4中,函数init_module()和函数cleanup_module()是必不可少的;而在Linux2.6中,同样功能的函数并非一定要起这两个函数名。Lab模块编程WriteTwoModule:Module1:HelloWorldModule.Load/unloadthemodulecanoutputsomeinfo.Module2:Moduleacceptsaparameter.Loadthemodule,outputtheparameter'svalue.ModifyModule1andModule2,letModule1exportssymbols,Module2usethesymbols.*注:详见实验指导二、中断和中断处理程序•中断处理的基本过程–Whenreceivinganinterrupt,CPUprogramcounterjumpstoapredefinedaddress(interruptvectors)–Thestateofinterruptedprogramissaved–Thecorrespondingserviceroutineisexecuted–Theinterruptingcomponentisserved,andinterruptsignalisremoved–Thestateofinterruptedprogramisrestored–Resumetheinterruptedprogramattheinterruptedaddress①中断描述符表IDT•中断描述符表是一个系统表,它与每一个中断或者异常向量相联系–每个向量在表中有相应的中断或者异常处理程序的入口地址。–每个描述符8个字节,共256项,占用空间2KB–内核在允许中断发生前,必须适当的初始化IDT•CPU的idtr寄存器指向IDT表的物理基地址•Interruptvectorsonx86初始化IDT•Linux内核在系统的初始化阶段要初始化可编程控制器8259A;–将中断描述符表的起始地址装入IDTR寄存器,–并初始化表中的每一项•当计算机运行在实模式时–IDT被初始化,并由BIOS使用。•真正进入了Linux内核–IDT就被移到内存的另一个区域,并为进入保护模式进行预初始化②中断处理程序•注册中断处理程序intrequest_ir
本文标题:Linux操作系统分析与实践
链接地址:https://www.777doc.com/doc-5098962 .html