实验报告1

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

实验:内核模块设计

1、实验要求

1.1 问题描述

设计一个带参数的内核模块,其参数为某进程号,列出其进程家族信息,包括父进程,兄弟进程以及子进程的程序名,PID号等。

1.2问题分析

Linux内核是模块化组成的,它允许内核在运行时动态地向其中插入或都从中删除代码。这些代码包括相关的子例程、数据、函数入口和函数出口被组合在一个单独的二进制镜像中,即是所谓的可以装载内核模块。支持模块的好处是基本内核镜像可以尽可能的小,因为可选的功能和驱动程序可以利用模块形式再提供。模块允许方便的和重新载入内核代码,也方便了调试工作。而且当热拔插新设备时,可以载入新的驱动程序。

Linux提供了这样一个简单的框架,它可以允许驱动程序声明参数,从而可以在系统启动或者模块装载时再指定参数,这些参数对于驱动程序属于全局变量,而且模块参数同时也将出现在sysfs文件系统中,这样,无论是生成模块参数还是管理模块参数的方式都变得灵活多样。

内核把进程放在一个叫task list的双向循环链表中。链表中的每一项都是task_struct称为进程描述符的结构。该结构定义在/include/linux/sched.h。进程描述符中包含一个具体进程的所有的信息。其中包括有进程号pid、指向父进程task_struct的指针parent、包含有所有子进程的链表结构children和包含父进程其它儿子进程的链表结构sibling。并且comm表示可以进行执行的文件的名字。

1.3 开发平台及实验工具

平台环境:VMware 7.1.3 build-324285

操作系统:CRUX 2.2

Linux内核版本:2.6.15.6

其它工具:KGDB2.4、Samba 3.0.24、Source Insight

2、实验步骤

(1),在/home/目录下编写内核模块程序。代码include如下头文件

(2),定义静态全局变量PID 用于接收模块初始化时传递过来的参数

(3),通过module_param(name,type,perm)宏来定义模块参数

PID是用户可见的参数名也是模块中存放模块参数的变量名,int是存放参数的类型。最后一个参数指定了模块在sysfs文件系统下对应的文件的权限。该值可以是8进制的格式,如0644,也使用S_IRUGO|S_IWUSR,表示任何人可读,user可写。如果该修士是0,则禁止所有sysfs项。

(4),定义模块初始化函数

通过for_each_process宏依次地访问整个任务队列。每一次访问任务指针都指向了链表中的下一个元素时检查其pid是不模块初始化进传递过来的PID。如果是,则找到该进程,退出for_each_process循环。当通过当前查找到的进程描述符读取comm输出进程的名字,通过parent读取父进程的进程描述符,输出父进程的pid和名字,从当前查找到的进程读取子进程列表children,通过list_for_each()宏来遍历子进程列表的所有元素,输出所有子进程的pid和名字。从当前查找到的进程读取兄弟进程列表sibling,通过list_for_each()宏来遍历兄弟进程列表的所有元素,输出所有兄弟进程的pid和名字。

(5),编写内核模块退出的函数

(6),将模块的初始化函数注册到系统中,在模块装载时被调用。调用module_init()实际不是真正的函数调用而是一个宏调用,它惟一的参数便是模块的初始化函数。出口函数通过module_exit()例程注册到系统中,在模块从内存卸载时内核便会调用该函数。

(7),在模块代码的目录下,建立Makefile文件,添加一行指令

obj-m :=module.c

(8),在内核模块代码目录下进行编译

make –C /usr/src/linux-2.6.15.6/ SUBDIRS=$PWD modules

(9), 载入模块,使用insmod module PID=3,请求内核载入指定的模块。

3、实验验证

先使用ps –A|more查看当前有哪些进程

查询进程号为513的“bash”shell命令处理进程的进程家族信息。输入insmod module PID=513,使用dmesg|tail查看日志得如下图结果

从结果中可以看出bash进程的父亲是init1进程,它由1号进程产生。它的子进程是刚才加载内核模块的insmod命令产生的进程。它的兄弟进程就是1号进程产生的其它所有进程,正是ps命令查看到的从2号到526进程(ps进程已经结束运行),也就是说ps命令查询到的从2号到526进程也都是由init 1进程产生的。

4、实验总结

Linux提供了这样一个简单的框架,它可以允许驱动程序声明参数,从而可以在系统启动或者模块装载时再指定参数,这些参数对于驱动程序属于全局变量,而且模块参数同时也将出现在sysfs文件系统中,这样,无论是生成模块参数还是管理模块参数的方式都变得灵活多样。

模块所有的初始化函数必须符合形式int my_init(void)。退出函数则必须满足符合void my_exit()。因为模块的初始化函数通常不会被外部函数直接调用,所以不必导出该函数,故它可以被标记为static。

如果内核模块的代码要被静态地编译到内核映像中,那么退出函数将不被包含,而且永远都不会被调用。因为如果不是编译成为模块的话那么代码不需要从内核中卸载。

内核把进程放在一个叫task list的双向循环链表中。链表中的每一项都是task_struct称为进程描述符的结构。该结构定义在/include/linux/sched.h。进程描述符中包含一个具体进程的所有的信息。其中包括有进程号pid、指向父进程task_struct的指针parent、包含有所有子进程的链表结构children和包含父进程其它儿子进程的链表结构sibling。并且comm表示可以进行执行的文件的名字。

由于Linux的进程有着父子兄弟的继承体系。所以从系统的任何一个进程出发可以查找到任意指定的其它进程。但是大多数时候,只需要通过简单的重复方式就可以遍历系统的所有进程。因为任务队列task_list就是一个双向循环链表,对于给定的进程,可以通过进程描述符中的next或者prev指针获得链表中的下一个进程。而实际上,Linux中for_each_process(task)宏提供了依次访问整个队列的能力。每次访问,任务指针都指向链表中的下一元素。

相关文档
最新文档