《模块编程实验》PPT课件.ppt

上传人:小飞机 文档编号:5536640 上传时间:2023-07-19 格式:PPT 页数:37 大小:280.50KB
返回 下载 相关 举报
《模块编程实验》PPT课件.ppt_第1页
第1页 / 共37页
《模块编程实验》PPT课件.ppt_第2页
第2页 / 共37页
《模块编程实验》PPT课件.ppt_第3页
第3页 / 共37页
《模块编程实验》PPT课件.ppt_第4页
第4页 / 共37页
《模块编程实验》PPT课件.ppt_第5页
第5页 / 共37页
点击查看更多>>
资源描述

《《模块编程实验》PPT课件.ppt》由会员分享,可在线阅读,更多相关《《模块编程实验》PPT课件.ppt(37页珍藏版)》请在三一办公上搜索。

1、模块编程实验,模块作为一种抽象数据类型,它具有一个可以通过静态内核中断的接口。最小的模块结构必须包括两个函数:init_module()和cleanup_module(),它们在系统加载模块和卸载模块时被调用。也可以编写一个只包括这两个函数的模块,这样该模块中唯一会被调用的函数就是模块被加载时所调用的函数init_module()和模块被卸载时所调用的函数cleanup_module()。并且用函数init_module()来启动模块加载期间的操作,用函数cleanup_module()来停止这些操作。由于模块可以实现相当复杂的功能,故可以在模块中加入很多新函数以实现所期望的功能。不过加入模块

2、的每个新函数都必须在该模块加载到内核中时进行注册。若该模块是静态加载的,则该模块的所有函数都是在内核启动时进行注册;若该模块是动态加载的,则这些新函数必须在加载这个模块时动态注册。当然,如果该模块被动态卸载了,则该模块的函数都必须从系统中注销。通过这种方式,当这个模块不在系统中时,就不能调用该模块的函数。其中注册工作通常是在函数init_module()中完成的,而注销工作则是在函数cleanup_module()中完成。,7.2.1 模块的组织结构,#include/说明是个内核功能#include/声明是一个模块/其它header信息 int init_module()/加载时,初始化模块

3、的编码/期望该模块所能实现的一些功能函数,如open()、release()、write()、read()、ioctl()等函数 void cleanup_module()/卸载时,注销模块的编码,一般编译模块文件的命令格式如下:#gcc-O2 g-Wall-DMODULE-D_KERNEL_-c f-I/usr/src/linux-2.4/include filename.c/filename.c为自己编写的模块程序源代码文件,7.2.2 模块的编译,7.2.3 模块的加载,7.2.4 模块的卸载,图7-1 模块链接到内核的示意图,7.2.5 模块链接到内核的示意图,在内核是用一个file结

4、构来识别模块,而且内核使用file_operations结构来访问模块程序中的函数。file_operations结构是一个定义在中的函数指针表。管理模块的文件操作,通常也称为“方法”,它们都为struct file_operations提供函数指针。在struct file_operations中的操作一般按如下顺序出现,除非特别说明,一般它们返回0值时表示访问成功;发生错误时会返回一个负的错误值(目前共有13个操作):int(*lseek)()、int(*read)()、int(*write)()int(*readdir)()、int(*select)()、int(*ioctl)()int

5、(*mmap)()、int(*open)()、void(*release)()int(*fsync)()、int(*fasync)()int(*check_media_change)()int(*revalidate)(),7.2.6 模块管理程序中的文件操作,struct file_operationsmodulename_fops=NULL,/modulename_lseek,改变模块结构中的操作位置。modulename_read,modulename_write,NULL,/modulename_readdir,读取某个子目录中的内容。NULL,/modulename_select,允

6、许应用程序响应来自模块的事件。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/module

7、name_revalidate,若更换了介质,则更新信息。,void(*release)(struct inode*,struct file*),7.3 实验内容,系统核心寄存器数值的获取,模块的编写#include/在内核模块中共享#include/一个模块/处理CONFIG_MODVERSIONS#if CONFIG_MODVERSIONS=1#define MODVERSIONS#include#endif int init_module()/初始化模块 printk(“Hello!This is a testing module!n”);return 0;void cleanup_mo

8、dule()/取消init_module()函数所做的打印功能操作 printk(“Sorry!The testing module is unloading now!n”);,7.4 实验指导,一个简单的内核模块,模块的编译、加载和卸载的过程如下:rootlinux/#gcc O2 Wall DMODULE D_KERNEL_-c testmodule.c rootlinux/#ls s/在当前目录下查看生成的目标文件testmodule.o 用下面命令将它加载到系统中:rootlinux/#insmod f testmodule.o 如果加载成功,则在/proc/modules文件中就可看

9、到模块testmodule,并可看到它的主设备号。同时在终端显示:Hello!This is a testing module!如果要卸载,就用如下命令:rootlinux/#rmmod testmodule 如果卸载成功,则在/proc/devices文件中就可看到模块testmodule已经不存在了。同时在终端显示:Sorry!The testing module is unloading now!,寄存器cr3存放着当前进程的“页表目录结构”的地址,这个值是在进程被唤醒的时候放入的。对它的读操作必须在内核空间中进行,否则将出现错误,本实验就是向读者展示这个错误。编写如下程序:#inclu

10、de/用户空间的标准输入输出头文件void GetCr3()int iValue;_asm_ _volatile_(movl%cr3,%0:=r(iValue);printf(“the value in cr3 is:%d”,a);/用户空间的标准输出函数 int main()GetCr3();Return 0;,7.4.2 模块加载前后的比较,对寄存器cr3的访问,必须在内核空间中完成,在用户程序中被禁止。而采用加载模块程序的方式就可以做到这一点。下面是程序源代码:文件名GetCr3.c。此外最好先建立一个目录。#include int init_module()int iValue;_as

11、m_ _volatile_(“movl%cr3,%0”:”=r”(iValue);printf(“cr3:%d”,iValue);return 0;void cleanup_module(void)printk(“uninstall getcr3!n”);,编写Makefile文件如下:DFLAGS=-D_KERNEL_-DMODULECFLAGS=-O2 g Wall Wstrict-prototypes pipe l/user/include/linux/GetCr3.o:GetCr3.cgcc c GetCr3.c$(DFLAGS)$(CFLAGS)o GetCr3.oclean:rm

12、f*.orootlinuxserver root#/sbin/insmod GetCr3.oCr3:234320012 rootlinuxserver root#/sbin/rmmod GetCr3Uninstall GetCr3!,函数open()int open(struct inode*inode,struct file*filp)MOD_INC_USE_COUNT;/增加该模块的用户数目 printk(“This module is in open!n”);return 0;函数release()void release(struct inode*inode,struct file*f

13、ilp)MOD_DEC_USE_COUNT;/该模块的用户数目减1 printk(“This module is in release!n”);return 0;#ifdef DEBUG printk(“release(%p,%p)n”,inode,filp);#endif,7.4.3 向模块中添加新函数,函数 read()int read(struct inode*inode,struct file*filp,char*buf,int count)int leave;if(verify_area(VERIFY_WRITE,buf,count)=DEFAULT)return DEFAULT;f

14、or(leave=count;leave0;leave-)_put_user(1,buf,1);buf+;return count;函数write()int write(struct inode*inode,struct file*filp,const char*buf,int count)return count;,7.4.4 模块的测试,在该模块程序编译加载后,再在/dev目录下创建模块设备文件moduledev,使用命令:#mknod/dev/moduledev c major minor其中“c”表示moduledev是字符设备,“major”是moduledev的主设备号。(该字符设

15、备驱动程序编译加载后,可在/proc/modules文件中获得主设备号,或者使用命令:rootlinux/#cat/proc/modules|awk”$2=”moduledev”print$1”获得主设备号),#include#include#include#include main()int i,testmoduledev;char buf10;testmoduledev=open(“/dev/moduledev”,O_RDWR);if(testmoduledev=-1)printf(“Cant open the file!n”);exit(0);read(testmoduledev,buf

16、,10);for(i=0;i10;i+)printf(“%dn”,bufi);close(testmoduledev);return 0;,模块程序设计思想 系统核心寄存器的数值在用户态下,对于这些寄存器数值的访问一般必须要在内核空间中完成,在用户程序中是被禁止的,因此将获取寄存器数值的嵌入式汇编的语句写入模块函数,这样在加载模块之后,用户程序就可以从内核空间中获得这些寄存器的数值。模块程序由open_get()、release_get()、read_get()、write_get()、init_module()、cleanup_module()以及一个数据结构Fops_Get七部分组成,各部

17、分的具体功能可见源程序中的注释。,7.4.5 系统核心寄存器数值的获取实验,file_operations结构的定义,当一个进程试图对生成的设备进行操作的时刻就利用下面这个数据结构,这个结构就是提供给操作系统的接口,它的指针保存在设备表中,在init_module()中被传递给操作系统。struct file_operations Fops_Get=read:device_read,write:device_write,open:device_open,release:device_release,;,头文件及程序定义,/*一些必要的头文件*/#include#include/*处理CONFI

18、G_MODVERSIONS*/#if CONFIG_MODVERSIONS=1#define MODVERSIONS#include#endif/*对于不同的版本需要做一些必要的事情*/#include#include#ifndef KERNEL_VERSION#define KERNEL_VERSION(a,b,c)(a)*65536+(b)*256+(c)#endif#if LINUX_VERSION_CODE KERNEL_VERSION(2,4,0)#include/*为了使用_put_user这个函数*/#endif,#define SUCCESS 0#define DEVICE_N

19、AME get_dev/*申明设备名,它会出现在/proc/devices*/#define BUF_LEN 100/*定义此设备消息缓冲的最大长度*/static int Open_Get=0;/*为了防止不同的进程在同一时间使用此设备,定义此静态变量跟踪其状态*/static char MessageBUF_LEN;/*当提出请求的时候,设备将读写的内容放在下面的数组*/static char*Message_Ptr;/*在进程读取设备内容的时候,这个指针是指向读取的位置*/static int Major;/*主设备号作为全局变量以便于这个设备在注册和释放的时候使用*/,头文件及程序定义

20、,open()函数,open()函数用于进程何时打开该设备。static int open_get(struct inode*inode,struct file*file)printk(This module is in openn);static int counter=0;#ifdef DEBUG printk(device_open(%p,%p)n,inode,file);#endif/*显示驱动模块的主设备号和次设备号*/printk(Device:%d.%dn,inode-i_rdev 8,inode-i_rdev,/*初始化信息,注意不要使用读写内容的长度超过缓冲区的长度,特别是运

21、行内核模式时,否则如果出现缓冲上溢则可能导致系统崩溃,因此在测试程序Test.c中只读取了10个字符*/sprintf(Message,If I told you once,I told you%d times-%s,counter+,Helo,worldn);Message_Ptr=Message;/*当这个文件被打开的时候,必须确认该模块没有被移走然后增加此模块的用户数目,与release()函数中的MOD_DEC_USE_COUNT;这条语句相对应。在执行cleanup_module()这个函数移去模块时,根据这个数字决定是否可移去,如果不是0则表明还有进程在使用这个模块,不能移走*/M

22、OD_INC_USE_COUNT;return SUCCESS;,open()函数,release()函数,release()函数用于进程关闭该设备特殊文件。#if LINUX_VERSION_CODE=KERNEL_VERSION(2,4,0)static int release_get(struct inode*inode,struct file*file)#else static void release_get(struct inode*inode,struct file*file)#endif printk(This module is in releasen);#ifdef DEB

23、UG printk(release_get(%p,%p)n,inode,file);#endif Open_Get-;/*为下一个使用这个设备的进程做准备*/*减少这个模块使用者的数目,否则将使得模块使用者的数目永远不会为0,就永远不能释放这个模块.与open()函数中的MOD_INC_USE_COUNT;这条语句相对应*/MOD_DEC_USE_COUNT;#if LINUX_VERSION_CODE=KERNEL_VERSION(2,4,0)return 0;#endif,read()函数,当打开此设备文件以后,read()函数用于读取数据,测试程序Test.c就是调用这个函数将10个字符

24、读入buf数组然后输出。#if LINUX_VERSION_CODE=KERNEL_VERSION(2,4,0)static ssize_t read_get(struct file*file,char*buffer,/*把读出的数据放到这个缓冲区,Test.c调用此函数时为数组buf*/size_t length,/*缓冲区的长度,Test.c调用此函数时赋值为10*/loff_t*offset)/*文件中的偏移*/#elsestatic int read_get(struct inode*inode,struct file*file,char*buffer,int length)#endi

25、f int i,bytes_read=0;/*i用于后边的循环,bytes_read是实际读出的字节数*/*验证buffer是否可用*/if(verify_area(VERIFY_WRITE,buffer,length)=-EFAULT)return-EFAULT;,read()函数,/*把用户的缓冲区全部写“7”,当然也可以写其他数字*/for(i=length;i0;i-)/*调用read()函数时,系统进入核心态,不能直接使用buffer这个地 址,必须用_put_user(),这是kernel提供的一个函数,用于向用户 传送数据.注意,有的内核版本中这个函数是三个参数*/_put_us

26、er(7,buffer);buffer+;/*地址指针向后移一位*/bytes_read+;/*读取的字节数增加1*/printk(Reading NO.%d character!n,bytes_read);return bytes_read;/*read()函数返回一个真正读出的字节数*/,write()函数,write()函数用于将数据写入着这个设备文件。但这里的write()函数是个空操作,实际调用时什么也不做,仅仅为Fops结构提供函数指针。#if LINUX_VERSION_CODE=KERNEL_VERSION(2,4,0)static ssize_t write_get(stru

27、ct file*file,const char*buffer,size_t length,loff_t*offset)#else static int write_get(struct inode*inode,struct file*file,const char*bffer,int length)#endif return length;,init_module()函数,init_module()这个函数用来初始化这个模块-注册该字符设备.init_module()函数调用nodule_register_chrdev,把设备驱动程序添加到内核的字符设备驱动表中,它返回这个驱动程序所使用的主设

28、备号。int init_module()printk(nHello!This is my module-Get!n);/*注册字符设备,注册后在/proc/devices中可以看到这个字符设备的主设备号*/Major=register_chrdev(0,DEVICE_NAME,init_module()函数,/*一些提示信息,由于我在虚拟机中编程时无法使用中文,所以使用英文提示*/Printk(%s The major device number is%d.nnn,Registeration is a succes:),Major);printk(If you want to talk to

29、the device driver,n);printk(youll have to creat a device file.n);printk(I suggest you use:n);printk(mknod c%d n,Major);printk(You can try different minor numbers%s,and something interesting will happen.nnn);printk(Here are the value of 23 important registers in my system:n);/*定义了23个整型变量用以存放寄存器的数值,并在

30、模块加载时显示在屏幕上*/int iValue01,iValue02,iValue03,iValue04,iValue05,iValue06,iValue07,iValue08,iValue09,iValue10,iValue11,iValue12,iValue13,iValue14,iValue15,iValue16,iValue17,iValue18,iValue19,iValue20,iValue21,iValue22,iValue23;,_asm_ _volatile_(movl%eax,%0:=r(iValue01);_asm_ _volatile_(movl%ebx,%0:=r(i

31、Value02);_asm_ _volatile_(movl%ecx,%0:=r(iValue03);_asm_ _volatile_(movl%edx,%0:=r(iValue04);_asm_ _volatile_(movl%esp,%0:=r(iValue05);_asm_ _volatile_(movl%ebp,%0:=r(iValue06);_asm_ _volatile_(movl%esi,%0:=r(iValue07);_asm_ _volatile_(movl%edi,%0:=r(iValue08);_asm_ _volatile_(movl%cs,%0:=r(iValue09

32、);_asm_ _volatile_(movl%ds,%0:=r(iValue10);_asm_ _volatile_(movl%ss,%0:=r(iValue11);_asm_ _volatile_(movl%es,%0:=r(iValue12);_asm_ _volatile_(movl%fs,%0:=r(iValue13);_asm_ _volatile_(movl%gs,%0:=r(iValue14);_asm_ _volatile_(movl%cr0,%0:=r(iValue15);_asm_ _volatile_(movl%cr2,%0:=r(iValue16);_asm_ _vo

33、latile_(movl%cr3,%0:=r(iValue17);_asm_ _volatile_(movl%dr0,%0:=r(iValue18);_asm_ _volatile_(movl%dr1,%0:=r(iValue19);_asm_ _volatile_(movl%dr2,%0:=r(iValue20);_asm_ _volatile_(movl%dr3,%0:=r(iValue21);_asm_ _volatile_(movl%dr6,%0:=r(iValue22);_asm_ _volatile_(movl%dr7,%0:=r(iValue23);,printk(EAX:%0

34、x,iValue01);printk(EBX:%0 x,iValue02);printk(ECX:%0 x,iValue03);printk(EDX:%0 x,iValue04);printk(ESP:%0 x,iValue05);printk(EBP:%0 xn,iValue06);printk(ESI:%0 x,iValue07);printk(EDI:%0 x,iValue08);printk(CS:%0 x,iValue09);printk(DS:%0 x,iValue10);printk(SS:%0 x,iValue11);printk(ES:%0 xn,iValue12);prin

35、tk(FS:%0 x,iValue13);printk(GS:%0 x,iValue14);printk(CR0:%0 x,iValue15);printk(CR2:%0 x,iValue16);printk(CR3:%0 x,iValue17);printk(DR0:%0 xn,iValue18);printk(DR1:%0 x,iValue19);printk(DR2:%0 x,iValue20);printk(DR3:%0 x,iValue21);printk(DR6:%0 x,iValue22);printk(DR7:%0 xnnn,iValue23);return 0;,cleanu

36、p_module()函数,cleanup_module()函数在卸载模块时被调用,主要是通过调用unregister_chrdev()从/proc中取消注册的设备文件。void cleanup_module()printk(Uninstall Get!Thanks you!n);int ret;/*取消设备文件的注册。被调用执行后可在/proc/devices里看到效果*/ret=unregister_chrdev(Major,DEVICE_NAME);/*异常处理*/if(ret 0)printk(Error in unregister_chrdev:%dn,ret);,Test.c设计思想

37、 Test.c就是通过对虚拟设备文件getdev进行操作,进而调用已经被加载进内核的驱动模块Get所提供的函数。首先调用open()函数打开模块文件,然后调用read()函数给用户空间的buffer数组赋值并将buffer数组输出到屏幕上,最后调用release()函数关闭模块文件。Test.c程序结构分析,测试源程序的设计与实现,#include/*C程序必要头文件*/*types.h头文件中定义了基本的数据类型。所有的类型定义为适当的数学类型长度。另外,size_t是无符号整数类型,off_t是扩展的符号整数类型,pid_t是符号整数类型。*/#include/*头文件stat.h说明了函

38、数stat()返回的数据及其结构类型,以及一些属性操作测试 宏、函数原型。*/#include#include/*与文件操作相关*/main()int i,testgetdev;char buf10;/*字符数组,用于获取从read()写入的数据*/testgetdev=open(/dev/getdev,O_RDWR);/*打开前边所注册的设备文件*/*异常处理*/if(testgetdev=-1)printf(I Cant open the file:(n);exit(0);,/*调用read()函数,read()函数将10个字符“7”写入用户的缓冲区buffer数组*/read(testgetdev,buf,10);/*输出数组buffer*/for(i=0;i10;i+)printf(NO.%d character is:%dn,i+1,bufi);close(testgetdev);/*事实上是调用release()函数关闭模块*/return 0;,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号