




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
模块编程实验
模块作为一种抽象数据类型,它具有一个可以通过静态内核中断的接口。最小的模块结构必须包括两个函数:init_module()和cleanup_module(),它们在系统加载模块和卸载模块时被调用。也可以编写一个只包括这两个函数的模块,这样该模块中唯一会被调用的函数就是模块被加载时所调用的函数init_module()和模块被卸载时所调用的函数cleanup_module()。并且用函数init_module()来启动模块加载期间的操作,用函数cleanup_module()来停止这些操作。由于模块可以实现相当复杂的功能,故可以在模块中加入很多新函数以实现所期望的功能。不过加入模块的每个新函数都必须在该模块加载到内核中时进行注册。若该模块是静态加载的,则该模块的所有函数都是在内核启动时进行注册;若该模块是动态加载的,则这些新函数必须在加载这个模块时动态注册。当然,如果该模块被动态卸载了,则该模块的函数都必须从系统中注销。通过这种方式,当这个模块不在系统中时,就不能调用该模块的函数。其中注册工作通常是在函数init_module()中完成的,而注销工作则是在函数cleanup_module()中完成。
7.2.1模块的组织结构一般编译模块文件的命令格式如下:#gcc-O2–g-Wall-DMODULE-D__KERNEL__-c–f-I/usr/src/linux-2.4/include//为自己编写的模块程序源代码文件
7.2.2模块的编译
7.2.3模块的加载7.2.4模块的卸载图7-1模块链接到内核的示意图7.2.5模块链接到内核的示意图在内核是用一个file结构来识别模块,而且内核使用结构来访问模块程序中的函数。结构是一个定义在<linux/fs.h>中的函数指针表。管理模块的文件操作,通常也称为“方法”,它们都为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模块管理程序中的文件操作7.3实验内容7.3.4
系统核心寄存器数值的获取模块的编写#include<linux/kernel.h>//在内核模块中共享
#include<linux/module.h>//一个模块
//处理CONFIG_MODVERSIONS#ifCONFIG_MODVERSIONS==1#defineMODVERSIONS#include<linux/modversions.h>#endifintinit_module()//初始化模块
{printk(“Hello!Thisisatestingmodule!\n”);
return0;
}voidcleanup_module()//取消init_module()函数所做的打印功能操作
{printk(“Sorry!Thetestingmoduleisunloadingnow!\n”);
}
7.4实验指导7.4.1一个简单的内核模块模块的编译、加载和卸载的过程如下:[root@linux/]#gcc–O2–Wall–DMODULE–D__KERNEL__-ctestmodule.c[root@linux/]#ls–s//在当前目录下查看生成的目标文件testmodule.o用下面命令将它加载到系统中:[root@linux/]#insmod–ftestmodule.o如果加载成功,则在/proc/modules文件中就可看到模块testmodule,并可看到它的主设备号。同时在终端显示:Hello!Thisisatestingmodule!如果要卸载,就用如下命令:[root@linux/]#rmmodtestmodule如果卸载成功,则在/proc/devices文件中就可看到模块testmodule已经不存在了。同时在终端显示:Sorry!Thetestingmoduleisunloadingnow!对寄存器cr3的访问,必须在内核空间中完成,在用户程序中被禁止。而采用加载模块程序的方式就可以做到这一点。下面是程序源代码:文件名GetCr3.c。此外最好先建立一个目录。#include<linux/module.h>intinit_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=-O2–g–Wall–Wstrict-prototypes–pipe–l/user/include/linux/GetCr3.o:GetCr3.cgcc–cGetCr3.c$(DFLAGS)$(CFLAGS)–oGetCr3.oclean: rm–f*.o[root@linuxserverroot]#/sbin/insmodGetCr3.oCr3:234320012[root@linuxserverroot]#/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);
#endif}
7.4.3向模块中添加新函数7.4.4模块的测试在该模块程序编译加载后,再在/dev目录下创建模块设备文件moduledev,使用命令:#mknod/dev/moduledevcmajorminor其中“c”表示moduledev是字符设备,“major”是moduledev的主设备号。(该字符设备驱动程序编译加载后,可在/proc/modules文件中获得主设备号,或者使用命令:[root@linux/]#cat/proc/modules|awk”\\$2==\”moduledev\”{print\\$1}”获得主设备号)#include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>main(){inti,testmoduledev;
charbuf[10];
testmoduledev=open(“/dev/moduledev”,O_RDWR);
if(testmoduledev==-1){printf(“Can’topenthefile!\n”);
exit(0);
}read(testmoduledev,buf,10);
for(i=0;i<10;i++)printf(“%d\n”,buf[i]);
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<linux/kernel.h>#include<linux/module.h>
/*处理CONFIG_MODVERSIONS*/#ifCONFIG_MODVERSIONS==1#defineMODVERSIONS#include<linux/modversions.h>#endif
/*对于不同的版本需要做一些必要的事情*/#include<linux/fs.h>#include<linux/wrapper.h>
#ifndefKERNEL_VERSION#defineKERNEL_VERSION(a,b,c)((a)*65536+(b)*256+(c))#endif#ifLINUX_VERSION_CODE>KERNEL_VERSION(2,4,0)#include<asm/uaccess.h>/*为了使用__put_user这个函数*/#endif#defineSUCCESS0#defineDEVICE_NAME"get_dev"/*申明设备名,它会出现在/proc/devices*/#defineBUF_LEN100/*定义此设备消息缓冲的最大长度*/
staticintOpen_Get=0;/*为了防止不同的进程在同一时间使用此设备,
定义此静态变量跟踪其状态*/staticcharMessage[BUF_LEN];/*当提出请求的时候,设备将读写的内容放在下面的数组*/staticchar*Message_Ptr;/*在进程读取设备内容的时候,这个指针是指向读取的位置*/staticintMajor;/*主设备号作为全局变量以便于这个设备在注册和释放的时候使用*/头文件及程序定义/*初始化信息,注意不要使用读写内容的长度超过缓冲区的长度,特别是运行内核模式时,否则如果出现缓冲上溢则可能导致系统崩溃,因此在测试程序Test.c中只读取了10个字符*/sprintf(Message,"IfItoldyouonce,Itoldyou%dtimes-%s",counter++,"Helo,world\n");Message_Ptr=Message;/*当这个文件被打开的时候,必须确认该模块没有被移走然后增加此模块的用户数目,与release()函数中的MOD_DEC_USE_COUNT;这条语句相对应。在执行cleanup_module()这个函数移去模块时,根据这个数字决定是否可移去,如果不是0则表明还有进程在使用这个模块,不能移走*/MOD_INC_USE_COUNT;returnSUCCESS;}open()函数release()函数release()函数用于进程关闭该设备特殊文件。#ifLINUX_VERSION_CODE>=KERNEL_VERSION(2,4,0)staticintrelease_get(structinode*inode,structfile*file)#elsestaticvoidrelease_get(structinode*inode,structfile*file)#endif{printk("Thismoduleisinrelease\n");#ifdefDEBUGprintk("release_get(%p,%p)\n",inode,file);#endif
Open_Get--;/*为下一个使用这个设备的进程做准备*/
/*减少这个模块使用者的数目,否则将使得模块使用者的数目永远不会为0,
就永远不能释放这个模块.与open()函数中的MOD_INC_USE_COUNT;
这条语句相对应*/MOD_DEC_USE_COUNT;#ifLINUX_VERSION_CODE>=KERNEL_VERSION(2,4,0)return0;#endif}read()函数当打开此设备文件以后,read()函数用于读取数据,测试程序Test.c就是调用这个函数将10个字符读入buf数组然后输出。#ifLINUX_VERSION_CODE>=KERNEL_VERSION(2,4,0)staticssize_tread_get(structfile*file,char*buffer,/*把读出的数据放到这个缓冲区,Test.c调用此函数时为数组buf[]*/size_tlength,/*缓冲区的长度,Test.c调用此函数时赋值为10*/loff_t*offset)/*文件中的偏移*/#elsestaticintread_get(structinode*inode,structfile**buffer,intlength)#endif{inti,bytes_read=0;/*i用于后边的循环,bytes_read是实际读出的字节数*//*验证buffer是否可用*/if(verify_area(VERIFY_WRITE,buffer,length)==-EFAULT)return-EFAULT;write()函数write()函数用于将数据写入着这个设备文件。但这里的write()函数是个空操作,实际调用时什么也不做,仅仅为Fops结构提供函数指针。#ifLINUX_VERSION_CODE>=KERNEL_VERSION(2,4,0)staticssize_twrite_get(structfile*char*buffer,size_tlength,loff_t*offset)#elsestaticintwrite_get(structinode*inode,structfile*char*bffer,intlength)#endif{returnlength;}init_module()函数init_module()这个函数用来初始化这个模块---注册该字符设备.init_module()函数调用nodule_register_chrdev,把设备驱动程序添加到内核的字符设备驱动表中,它返回这个驱动程序所使用的主设备号。intinit_module(){printk("\nHello!Thisismymodule---'Get'!\n");
/*注册字符设备,注册后在/proc/devices中可以看到这个字符设备的主设备号*/Major=register_chrdev(0,DEVICE_NAME,&Fops_Get);/*异常处理*/if(Major<0){printk("%sdevicefailedwith%d\n","Sorry,registeringthecharacter:(",Major);returnMajor;}init_module()函数/*一些提示信息,由于我在虚拟机中编程时无法使用中文,所以使用英文提示*/Printk("%sThemajordevicenumberis%d.\n\n\n","Registerationisasucces:)",Major);printk("Ifyouwanttotalktothedevicedriver,\n");printk("you'llhavetocreatadevicefile.\n");printk("Isuggestyouuse:\n");printk("mknod<name>c%d<minor>\n",Major);printk("Youcantrydifferentminornumbers%s","andsomethinginterestingwillhappen.\n\n\n");printk("Herearethevalueof23importantregistersinmysystem:\n");
/*定义了23个整型变量用以存放寄存器的数值,并在模块加载时显示在屏幕上*/intiValue01,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"(iValue02));__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));__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____volatile__("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:%0x",iValue01);printk("EBX:%0x",iValue02);printk("ECX:%0x",iValue03);printk("EDX:%0x",iValue04);printk("ESP:%0x",iValue05);printk("EBP:%0x\n",iValue06);printk("ESI:%0x",iValue07);printk("EDI:%0x",iValue08);printk("CS:%0x",iValue09);printk("DS:%0x",iValue10);printk("SS:%0x",iValue11);printk("ES:%0x\n",iValue12);printk("FS:%0x",iValue13);printk("GS:%0x",iValue14);printk("CR0:%0x",iValue15);printk("CR2:%0x",iValue16);printk("CR3:%0x",iValue17);printk("DR0:%0x\n",iValue18);printk("DR1:%0x",iValue19);printk
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年度股份代持与股权激励实施方案协议
- 2025年度足疗中心员工工资保底与员工满意度调查协议
- 房产证抵押贷款额度调整协议(2025年度)
- 2025年度食品包装设计及委托加工合同
- 二零二五年度储蓄存款业务创新激励机制合同
- 二零二五年度银行账户监管协议:银行账户资金监管与网络安全保障合同
- 二零二五年度智能物流件代发合作协议
- 二零二五年度体育赛事运营补充协议范本
- 二零二五年度茶饮连锁品牌全国代理权独家协议
- 幼儿园学生人身安全赔偿协议范本2025
- 数学与体育融合课程设计
- 七年级英语阅读理解专项训练(含答案)共20篇
- 初步设计法律规范
- 社区获得性肺炎疾病查房
- 神奇的光:如何形成彩虹
- 三、胆石症课件
- 兔子坡(阅读课上课课件)
- 固定资产清查盘点明细表
- 起重吊装施工重难点及管控措施
- (理实)《Java程序设计》图形用户界面(GUI)设计 课件
- 《封神演义》与道教神仙体系
评论
0/150
提交评论