
模块编程实验课件.ppt
37页模块编程实验 模块作为一种抽象数据类型,它具有一个可以通过静态内核中断的接口最小的模块结构必须包括两个函数:init_module()和cleanup_module(),它们在系统加载模块和卸载模块时被调用也可以编写一个只包括这两个函数的模块,这样该模块中唯一会被调用的函数就是模块被加载时所调用的函数init_module()和模块被卸载时所调用的函数cleanup_module()并且用函数init_module()来启动模块加载期间的操作,用函数cleanup_module()来停止这些操作由于模块可以实现相当复杂的功能,故可以在模块中加入很多新函数以实现所期望的功能不过加入模块的每个新函数都必须在该模块加载到内核中时进行注册若该模块是静态加载的,则该模块的所有函数都是在内核启动时进行注册;若该模块是动态加载的,则这些新函数必须在加载这个模块时动态注册当然,如果该模块被动态卸载了,则该模块的函数都必须从系统中注销通过这种方式,当这个模块不在系统中时,就不能调用该模块的函数其中注册工作通常是在函数init_module()中完成的,而注销工作则是在函数cleanup_module()中完成。
7.2.1 模块的组织结构#include/说明是个内核功能#include/声明是一个模块/其它header信息intinit_module()/加载时,初始化模块的编码/期望该模块所能实现的一些功能函数,如open()、release()、write()、read()、ioctl()等函数voidcleanup_module()/卸载时,注销模块的编码一般编译模块文件的命令格式如下:#gcc-O2g-Wall-DMODULE-D_KERNEL_-cf-I/usr/src/linux-2.4/include/为自己编写的模块程序源代码文件7.2.2 模块的编译 7.2.3 模块的加载7.2.4 模块的卸载图7-1模块链接到内核的示意图7.2.5 模块链接到内核的示意图在内核是用一个file结构来识别模块,而且内核使用结构来访问模块程序中的函数结构是一个定义在中的函数指针表管理模块的文件操作,通常也称为“方法”,它们都为struct 提供函数指针在struct 中的操作一般按如下顺序出现,除非特别说明,一般它们返回0值时表示访问成功;发生错误时会返回一个负的错误值(目前共有13个操作):int(*lseek)()、int(*read)()、int(*write)()int(*readdir)()、int(*select)()、int(*ioctl)()int(*mmap)()、int(*open)()、void(*release)()int(*fsync)()、int(*fasync)()int(*check_media_change)()int(*revalidate)()7.2.6 模块管理程序中的文件操作structmodulename_fops=NULL,/modulename_lseek,改变模块结构中的操作位置。
modulename_read,modulename_write,NULL,/modulename_readdir,读取某个子目录中的内容NULL,/modulename_select,允许应用程序响应来自模块的事件NULL,/modulename_ioctl,应用程序通过I/O控制系统的系统调用来控制模块行为NULL,/modulename_mmap,模块地址空间到用户地址空间的映射modulename_open,modulename_release,NULL,/modulename_fsync,同步内存与磁盘上的数据状态,把输出缓冲区里尚未写到磁盘的数据写出去NULL,/modulename_fasync,改变模块行为NULL,/modulename_check_media_change,检查自上次操作后,介质(软盘和CD-ROM)是否更换NULL/modulename_revalidate,若更换了介质,则更新信息void (*release)(struct inode *,struct file *) 7.3 实验内容7.3.4系统核心寄存器数值的获取模块的编写#include/在内核模块中共享#include/一个模块/处理CONFIG_MODVERSIONS#ifCONFIG_MODVERSIONS=1#defineMODVERSIONS#include#endifintinit_module()/初始化模块printk(“Hello!Thisisatestingmodule!n”);return0;voidcleanup_module()/取消init_module()函数所做的打印功能操作printk(“Sorry!Thetestingmoduleisunloadingnow!n”);7.4 实验指导7.4.1一个简单的内核模块模块的编译、加载和卸载的过程如下:rootlinux/#gccO2WallDMODULED_KERNEL_-ctestmodule.crootlinux/#lss/在当前目录下查看生成的目标文件testmodule.o用下面命令将它加载到系统中:rootlinux/#insmodftestmodule.o如果加载成功,则在/proc/modules文件中就可看到模块testmodule,并可看到它的主设备号。
同时在终端显示:Hello!Thisisatestingmodule!如果要卸载,就用如下命令:rootlinux/#rmmodtestmodule如果卸载成功,则在/proc/devices文件中就可看到模块testmodule已经不存在了同时在终端显示:Sorry!Thetestingmoduleisunloadingnow!寄存器cr3存放着当前进程的“页表目录结构”的地址,这个值是在进程被唤醒的时候放入的对它的读操作必须在内核空间中进行,否则将出现错误,本实验就是向读者展示这个错误编写如下程序:#include/用户空间的标准输入输出头文件voidGetCr3()intiValue;_asm_volatile_(movl%cr3,%0:=r(iValue);printf(“thevalueincr3is:%d”,a);/用户空间的标准输出函数intmain()GetCr3();Return0;7.4.2 模块加载前后的比较对寄存器cr3的访问,必须在内核空间中完成,在用户程序中被禁止而采用加载模块程序的方式就可以做到这一点下面是程序源代码:文件名GetCr3.c此外最好先建立一个目录。
includeintinit_module()intiValue;_asm_volatile_(“movl%cr3,%0”:”=r”(iValue);printf(“cr3:%d”,iValue);return0;voidcleanup_module(void)printk(“uninstallgetcr3!n”);编写Makefile文件如下:DFLAGS=-D_KERNEL_-DMODULECFLAGS=-O2gWallWstrict-prototypespipel/user/include/linux/GetCr3.o:GetCr3.cgcccGetCr3.c$(DFLAGS)$(CFLAGS)oGetCr3.oclean:rmf*.orootlinuxserverroot#/sbin/insmodGetCr3.oCr3:234320012rootlinuxserverroot#/sbin/rmmodGetCr3UninstallGetCr3!函数open( ) intopen(structinode*inode,structfile*filp)MOD_INC_USE_COUNT;/增加该模块的用户数目printk(“Thismoduleisinopen!n”);return0;函数release( ) voidrelease(structinode*inode,structfile*filp)MOD_DEC_USE_COUNT;/该模块的用户数目减1printk(“Thismoduleisinrelease!n”);return0;#ifdefDEBUGprintk(“release(%p,%p)n”,inode,filp);#endif7.4.3 向模块中添加新函数函数 read()intread(structinode*inode,structfile*filp,char*buf,intcount)intleave;if(verify_area(VERIFY_WRITE,buf,count)=DEFAULT)returnDEFAULT;for(leave=count;leave0;leave-)_put_user(1,buf,1);buf+;returncount;函数write() intwrite(structinode*inode,structfile*filp,constchar*buf,intcount)returncount;7.4.4 模块的测试 在该模块程序编译加载后,再在/dev目录下创建模块设备文件moduledev,使用命令:#mknod/dev/moduledevcmajorminor其中“c”表示moduledev是字符设备,“major”是moduledev的主设备号。
该字符设备驱动程序编译加载后,可在/proc/modules文件中获得主设备号,或者使用命令:rootlinux/#cat/proc/modules|awk”$2=”moduledev”print$1”获得主设备号)#include#include#include#includemain()inti,testmoduledev;charbuf10;testmoduledev=open(“/dev/moduledev”,O_RDWR);if(testmoduledev=-1)printf(“Cantopenthefile!n”);exit(0);read(testmoduledev,buf,10);for(i=0;i10;i+)printf(“%dn”,bufi);close(testmoduledev);return0;模块程序设计思想系统核心寄存器的数值在用户态下,对于这些寄存器数值的访问一般必须要在内核空间中完成,在用户程序中是被禁止的,因此将获取寄存器数值的嵌入式汇编的语句写入模块函数,这样在加载模块之后,用户程序就可以从内核空间中获得这些寄存器的数值模块程序由open_get()、release_get()、read_get()、write_get()、init_module()、cleanup_module()以及一个数据结构Fops_Get七部分组成,各部分的具体功能可见源程序中的注释。
7.4.5 系统统核心寄存器数值值的获获取实验实验结构的定义当一个进程试图对生成的设备进行操作的时刻就利用下面这个数据结构,这个结构就是提供给操作系统的接口,它的指针保存在设备表中,在init_module()中被传递给操作系统structFops_Get=read:device_read,write:device_write,open:device_open,release:device_release,;头文件及程序定义/*一些必要的头文件*/#include#include/*处理CONFIG_MODVERSIONS*/#ifCONFIG_MODVERSIONS=1#defineMODVERSIONS#include#endif/*对于不同的版本需要做一些必要的事情*/#include#include#ifndefKERNEL_VERSION#defineKERNEL_VERSION(a,b,c)(a)*。












