为什么要发布我的报告?
按理说操作系统实验应该自己做,这样能锻炼自己。鉴于我的报告还是比较有参考价值,能让以后的同学参考一下,就做成md的形式。仅供参考
一、实验内容:
(1)实验名:Linux内核模块编程
(2)实验要求:
1)设计一个模块,要求列出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的PID。
2)设计一个带参数的模块,其参数为某个进程的PID号,模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID号及进程状态。
二、实验思路
(1)使用for_each_process()遍历所有进程,输出那些mm值为NULL的进程信息。
for_each_process是宏循环控制语句,在/include/linux/sched/signal.h中:
#define for_each_process(p) \
for (p = &init_task ; (p = next_task(p)) != &init_task ; )
其中next_task也是宏循环控制语句,在/include/linux/sched/signal.h中:
#define next_task(p) \
list_entry_rcu((p)->tasks.next, struct task_struct, tasks)
而task_struct中有:
struct task_struct {
...
struct list_head tasks;
...
}
而list_entry就是从循环链表中读取实际值,后面也会用到。。
(2)从实验一我们就已经知道 使用find_get_pid()和pid_task()得到某个进程的task_struct,我们把其叫做task。
1)task->parent即为该进程的父进程,类型为struct task_struct,可以直接用。
2)task->sibling是一个struct list_head类型的值,表示链表的头部,链表中的所有元素都是它的兄弟进程。
在/include/linux/types.h目录下我们找到list_head的定义:
struct list_head {
struct list_head *next, *prev;
};
这个双向链表竟然没有指向实质内容的结构体指针??通过网上查阅资料,我们知道可以用list_entry()得到实际的值。
3)另外,我们需要清楚循环链表中有什么。书上P59页非常清楚的给出了这个兄弟循环链表的图示:
可以看出,循环链表中不仅有所有兄弟的sibling,还有父进程的children这一项,在遍历时需要加个判断条件去除。(通过查找网上资料我们知道:sibling.next指向进程的下一个兄弟进程的进程描述符sibling成员,若其后没有其他兄弟进程,则指向父进程;而sibling.prev指向进程的上一个兄弟进程,若其之前没有兄弟进程,则指向父进程。)
(3)求子进程的方法和(2)其实差不多,但是有一点需要注意:
children.next指向父进程的第一个子进程的sibling成员(而不是children成员!),而children.prev却指向父进程的最后一个子进程的sibling成员。
因此在使用list_entry()获得task_struct指针时,参数要用sibling而不是children。
三、内核模块编程实验步骤及问题
(1)查看关于task_struct结构体与相关函数的源码。
(2)写代码并调试。
遇到的错误及解决:在做兄弟进程的时候,出现乱码问题。我查阅资料,发现兄弟进程队列中竟然有指向父节点的一项。加个if判断即可。
(3)测试方法:
1)用pstree -p -n -T命令打开文件树,-T去除线程,-p显示pid
2)找一个合适的进程pid
3)按照书上说的方法运行我们写的函数。
4)查看结果。
五、实验总结
经过实验一,我发现网上的代码也有可能错误,于是这次实验决定不照抄网上的代码,先看原理,理解了再看别人的代码,再判断他的代码是否可行,最后再自己写。
其实除了要用到的知识点,通过查看task_struct这一结构体,我们还可以了解以下这些:
(1)task_struct中有两个父亲进程指针:
/*父进程*/
struct task_struct __rcu *real_parent;//指向其父进程,如果创建它的父进程不再存在,则指向PID为1的init进程,rcu为锁机制
struct task_struct __rcu *parent;//指向其父进程,当它终止时,必须向它的父进程发送信号。它的值通常与real_parent相同
(2)task_struct中当然也有和文件系统相关的内容,如:
struct fs_struct *fs;//表示进程与文件系统的联系,如进程的当前目录和根目录
struct files_struct *files;//记录进程当前打开的文件
(3)mm其实也有两个:
struct mm_struct *mm;//指向进程用户地址空间描述符,指向内存块
struct mm_struct *active_mm;//指向进程运行时所使用的内存描述符
/*对于普通进程而言,这两个指针变量的值相同。但是,内核线程不拥有任何内存描述符,所以它们的mm成员总是为NULL。当内核线程得以运行时,它的active_mm成员被初始化为前一个运行进程的active_mm值。
*/
(4)当然也有关于进程调度相关的内容:
int on_rq; //runquene,可执行队列,它是调度程序中最基本的数据结构。
/* ...进程优先级... */
unsigned int policy;//进程调度策略(见后面)
/*
可以通过系统调用sys_sched_setscheduler()更改(见kernel/sched.c)。调度策略有:
SCHED_OTHER 0 非实时进程,基于优先权的轮转法(round robin)。
SCHED_FIFO 1 实时进程,用先进先出算法。
SCHED_RR 2 实时进程,用基于优先权的轮转法。
*/
六、附录
(1)Demo1
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/sched/signal.h>
static int show_all_process_init(void)
{
struct task_struct *p;
p=NULL;
printk(KERN_ALERT"show_all_process_init() begin");
for_each_process(p){
if(p->mm==NULL)
{
printk(KERN_ALERT"%20s %5d %5ld %5d %5d\n",p->comm,p->pid, p->state,p->prio,p->parent->pid);
}
}
printk(KERN_ALERT"\n");
return 0;
}
static void show_all_process_exit(void)
{
printk(KERN_ALERT"show_all_process_init() has exited\n");
}
module_init(show_all_process_init);
module_exit(show_all_process_exit);
MODULE_LICENSE("GPL");
(2)Demo2
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/pid.h>
#include<linux/types.h>
#include<linux/sched.h>
#include<linux/list.h>
#include<linux/moduleparam.h>
static int pid;
module_param(pid, int ,0644);
static int show_family_init(void)
{
struct pid * kpid;
struct task_struct * task;
struct task_struct *parent;
struct list_head *p_sibling;
struct task_struct *sibling_task;
struct list_head *p_children;
struct task_struct *children_task;
kpid = find_get_pid(pid);
task = pid_task(kpid, PIDTYPE_PID);
printk(KERN_ALERT"show_family_init() begin");
//父进程
printk(KERN_ALERT"parent process:\n");
parent = task->parent;
printk(KERN_ALERT"parent->comm parent->pid patent->state\n");
printk("%20s %5d %5ld\n",parent->comm,parent->pid,parent->state);
//兄弟进程
printk(KERN_ALERT"brother process:\n");
printk(KERN_ALERT"sibling_task->comm sibling_task->pid sibling_task->state\n");
list_for_each(p_sibling,&(task->sibling))
{
if(p_sibling != &task->parent->children)
{
sibling_task=list_entry(p_sibling,struct task_struct,sibling);
//if(sibling_task->pid != task->parent->children->pid)
printk("%20s %5d %5ld\n",sibling_task->comm,sibling_task->pid,sibling_task->state);
}
}
//子进程
printk(KERN_ALERT"children process:\n");
printk(KERN_ALERT"children_task->comm children_task->pid children_task->state\n");
list_for_each(p_children,&(task->children))
{
children_task=list_entry(p_children,struct task_struct,sibling);
printk("%20s %5d %5ld\n",children_task->comm,children_task->pid,children_task->state);
}
return 0;
}
static void show_family_exit(void)
{
printk(KERN_ALERT"show_family_init() has exited\n");
}
module_init(show_family_init);
module_exit(show_family_exit);
MODULE_LICENSE("GPL");