Bootstrap

杭电操作系统实验二报告

为什么要发布我的报告?
按理说操作系统实验应该自己做,这样能锻炼自己。鉴于我的报告还是比较有参考价值,能让以后的同学参考一下,就做成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");

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;