实验三添加内核模块一、实验目的:学习Linux模块的基本概念和原理,学习内核模块编程的基本技术,利用内核模块编程访问进程描述符,操作内核的基本数据结构,加深对进程的理解;理解proc文件系统的作用,学习proc文件的创建方法,掌握这种用户态和核心态通信的方法。二、实验平台:虚拟机:VMWare9操作系统:Ubuntu12.04编辑器:Gedit|Vi三、实验内容:(1)阅读内核模块实例hello.c,掌握内核模块的主要构成;阅读Makefile文件,理解内核模块的编译方法及执行过程;掌握模块安装、卸载,以及查看模块信息的方法。查看模块信息:卸载模块:(2)设计一个模块,功能是列出系统中所有内核进程的程序名、PID号和进程状态。主要步骤:阅读内核源代码,了解进程描述符task_struct中与本实验有关的成员项,以及访问进程队列的宏for_each_process;编写readprocess模块,获取进程信息;修改Makefile文件,编译、安装模块,查看输出信息;查看模块信息,卸载模块。readprocess.c:#includelinux/init.h#includelinux/module.h#includelinux/kernel.h#includelinux/sched.h#includelinux/init_task.h//初始化函数staticinthello_init(void){structtask_struct*p;p=NULL;p=&init_task;printk(KERN_ALERT名称\t进程号\t状态\t优先级\t父进程号\t);for_each_process(p){if(p-mm==NULL){//内核线程的mm成员为空printk(KERN_ALERT%s\t%d\t%ld\t%d\n,p-comm,p-pid,p-state,p-normal_prio,p-parent-pid);}}return0;}//清理函数staticvoidhello_exit(void){printk(KERN_ALERTgoodbye!\n);}//函数注册module_init(hello_init);module_exit(hello_exit);//模块许可申明MODULE_LICENSE(GPL);Makefile代码:ifneq($(KERNELRELEASE),)obj-m:=readprocess.oelseKDIR:=/lib/modules/$(shelluname-r)/buildPWD:=$(shellpwd)default:$(MAKE)-C$(KDIR)M=$(PWD)modulesclean:$(MAKE)-C$(KDIR)M=$(PWD)cleanendif将process.c和Makefile文件放在同一个文件夹下使用make函数生成后缀为.ko文件:使用命令载入模块,如图:使用lsmod命令显示载入系统的模块,如图:(3)利用内核模块编程,在/proc目录下用自己的学号创建一个目录,如/proc/201300834101然后在学号目录下创建一个processinfo文件,如/proc/201300834101/processinfo,此文件为只读文件,用于显示所有内核进程的程序名、PID号和进程状态。主要步骤:修改(2)中readprocess模块,在模块初始化函数中创建目录及proc文件,并定义产生proc文件内容的函数(获取进程信息);在卸载模块函数中删除相应的proc文件及目录;修改Makefile文件,编译、安装模块;Processinfo.c:#includelinux/module.h//初始化模块#includelinux/proc_fs.h//创建进程信息入口#includelinux/sched/task.h//初始进程#includelinux/seq_file.h//序列文件#includelinux/slab.h//内存分配释放#includelinux/sched/signal.h//下一个进程charmodname[]=201608030317;structtask_struct*task;inttaskcounts=0;//全局进程变量staticvoid*my_seq_start(structseq_file*m,loff_t*pos){///printk(KERN_INFOInvokestart\n);//可以输出调试信息if(*pos==0)//表示遍历开始{task=&init_task;//遍历开始的记录地址return&task;//返回一个非零值表示开始遍历}else//遍历过程中{if(task==&init_task)//重新回到初始地址,退出returnNULL;return(void*)pos;//否则返回一个非零值}}staticintmy_seq_show(structseq_file*m,void*v){//获取进程的相关信息//printk(KERN_INFOInvokeshow\n);seq_printf(m,#%-3d\t,taskcounts);//输出进程序号seq_printf(m,%d\t,task-pid);//输出进程pidseq_printf(m,%lu\t,task-state);//输出进程stateseq_printf(m,%s\t,task-comm);//输出进程名称(comm)seq_puts(m,\n);return0;}staticvoid*my_seq_next(structseq_file*m,void*v,loff_t*pos){//printk(KERN_INFOInvokenext\n);(*pos)++;//task指向下一个进程?taskcounts++;task=next_task(task);//指向下一个进程returnNULL;}staticvoidmy_seq_stop(structseq_file*m,void*v){//printk(KERN_INFOInvokestop\n);//donothing}staticstructseq_operationsmy_seq_fops={//序列文件记录操作函数集合.start=my_seq_start,.next=my_seq_next,.stop=my_seq_stop,.show=my_seq_show};staticintmy_open(structinode*inode,structfile*file){returnseq_open(file,&my_seq_fops);//打开序列文件并关联my_seq_fops}staticconststructfile_operationsmy_proc={//proc文件操作函数集合.owner=THIS_MODULE,.open=my_open,.read=seq_read,.llseek=seq_lseek,.release=seq_release};int__initmy_init(void){structproc_dir_entry*my_proc_entry;printk(1\nInstalling\'%s\'module\n,modname);my_proc_entry=proc_create(modname,0x644,NULL,&my_proc);//生成proc文件if(NULL==my_proc_entry){return-ENOMEM;}return0;//SUCCESS}void__exitmy_exit(void){remove_proc_entry(modname,NULL);//删除proc文件printk(1Removing\'%s\'module\n,modname);}module_init(my_init);module_exit(my_exit);MODULE_LICENSE(GPL);Makefile代码:ifneq($(KERNELRELEASE),)obj-m:=readprocess.oelseKDIR:=/lib/modules/$(shelluname-r)/buildPWD:=$(shellpwd)default:$(MAKE)-C$(KDIR)M=$(PWD)modulesclean:$(MAKE)-C$(KDIR)M=$(PWD)cleanendif将processinfo.c和Makefile文件放在同一个文件夹下使用make函数生成后缀为.ko文件:查看模块是否存在:使用dmesg命令查看到系统的内核模块信息,如图:查看进程信息:#cat/proc/201608030317/processinfo卸载模块后:四.实验心得通过本次实验我学习Linux模块的基本概念和原理,学习内核模块编程的基本技术,利用内核模块编程访问进程描述符,操作内核的基本数据结构,加深对进程的理解;理解proc文件系统的作用,学习proc文件的创建方法,掌握这种用户态和核心态通信的方法。对Linux系统有了更深的了解。