关于进程树(pstree)的剖析

来源:互联网 发布:vue.js常用开发工具 编辑:程序博客网 时间:2024/06/06 01:38

/*******************************************************************************************//*

代码及编写过程小结:

1.在使用sibling进程链时发现这个链有很严重的不可塑性,属于同一个父进程的所有子进程的sibling链都不相同,长短不一,而且链上的进程也毫无规律(至少我没找到)所以使用sibling链的结果就是只能打印部分进程,做不到遍历,于是放弃sibling

2.于是只剩下了children链可用,不过它的稳定性和一致性还是很让人放心的。但简单的数据结构就意味着要面临复杂的算法问题:

(a)在做深度优先遍历时,通过list_entry()做循环可寻找到叶子节点,但如何向根方向回溯却成了问题,我使用了一个栈来存放祖先节点(从根节点到该节点所经分支上的所有节点),拥有了祖先节点,用几个push,pop回溯和遍历看起来就不是问题了就不是问题了。

 (b)拥有了祖先节点,但却无法避免以某一祖先节点为选定节点进入另一树杈时却进入了原来已经扫过的树杈。这里想到两种解决方法,一是将已扫过树杈进行标记,但考虑到需要定义一个结构体,就犹豫了。于是有了第二种方法---在存放祖先节点的栈里并不存放祖先节点,而是存放(祖先节点)->next。至于这种方法的可行性,我用自己幼稚的框图法演绎了无数遍,给自己了一个肯定的答案。不知是不是创新O(∩_∩)O

 (c)剩下的就是一些细节问题了,比如递归的出口啊,判断条件啊什么的,应该做的还算规矩……

  (d)输出格式的缩进没有加在代码里,但是在我的结构里确实非常好做的,因为我保存了节点所处层次,就是祖先节点栈(其实是数组)里的序号i!相应的,i等于几,就缩进几个空格。

ps:除了没办法运行,我还是挺喜欢我的代码的

*//*******************************************************************************************/

 

 

#ifndef __KERNEL__

#define __KERNEL__

#endif

#ifndef MODULE

#define MODULE

#endif

#include <linux/sched.h>

#include <linux/unistd.h>

#include <linux/list.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

 

MODULE_LICENSE("Dual BSD/GPL");

int i=0;

struct list_head* h

/*自己建的一个粗糙的栈*/

struct stk

{struct list_head* heap[500];//500个指向list_head结构体的指针

}stack;

//push

void push(struct list_head* a)

{

if(i<500)

i++;

stack.heap[i]=a;

}

//pop

struct list_head* pop(void)

{

struct list_head* pp;

if(i<=0)

return NULL;

else

{pp=stack.heap[i];

 i--;}

return pp;

}

//遍历

int kprint(struct list_head* l,structtask_struct *p)

{

 p=list_entry(l,struct task_struct, sibling);//list_entry的实现比较难懂

 printk("%d-%d--%s\n",p->parent->pid,p->pid,p->comm);//print

 if(&(p->children)==p->children.next)//struct中->和.的区别:“->”的使用时在这种情况下:

 {  h=pop();                   //p是指向struct A的指针,则访问struct中元素即用p->a //的方式。“.”的使用是在这种情况下:p是定义为struct //A的变量,则访问struct中的元素即用p.a的方式 ,这//里的疑问就是2.6中为什么要将children和sibling均定//义为list_head变量而非指向该结构的指针                                         

                               //structlist_head 的定义为

                               //struct list_head{structlist_head* next, *prev;}

//猜想如下:list_head和传统链表的区别在于链表结构//作为一个成员嵌入到宿主数据结构内链表结构可放在//宿主结构内的任何位置;一个宿主结构可有多个内核//链表结构。通用双向链表优点:避免为每个数据项类//型定义自己的链表,故list_head不能够定义为指针

 

  if(h=NULL)

  return 0;//out

  else

  if(&(p->parent->children)!=h)

   {

  push(h->next);

  kprint(h,p);

  }//进入该节点的兄弟结点

 

 else

 {h=pop();printk("ip=%d\n",i);

 if(h==NULL)

  return 0;//out,其实和上面out是一样的

  else

   {push(h->next);printk("i=%d\n",i);

    kprint(h,p);}

   }//退回父节点的兄弟结点

 }

else

 {h=p->children.next;

  push(h->next);printk("i=%d\n",i);

  kprint(h,p,h);//进入子节点

  }

}

static int pstree_init(void)

{

struct task_struct *p;

struct list_head* l;

for ( p = current; p != &init_task; p =p->parent ) ;

l=p->parent->children.next;

h=l->next;

kprint(l,p);

return 0;

}

 

static void pstree_exit(void)

   printk("Hello, kernel!\n");

}

module_init(pstree_init);

module_exit(pstree_exit);

 

/*******************************************************************************************//*

实例代码

*//*******************************************************************************************/

#define __KERNEL__

 

#endif

 

#ifndef MODULE

 

#define MODULE

 

#endif //首先明确这是一个模块

 

#include <linux/sched.h>

 

#include <linux/unistd.h>

 

#include <linux/list.h>

 

#include <linux/init.h>

 

#include <linux/module.h>//任何模块程序的编写都需要包含linux/module.h这个头文件

 

#include <linux/kernel.h>

 

MODULE_LICENSE("Dual BSD/GPL");//

 

void pstreea(struct task_struct* p,int b)

 

{int i;

 

for(i=1;i<=b;i++)

 

printk("   ");

 

printk("|---%s/n",p->comm);

 

struct list_head* l;

 

for (l = p->children.next; l!=&(p->children);l = l->next)//作用同list_for_each(),l在children链上//移动

{struct task_struct* t=list_entry(l,structtask_struct,sibling);//将children链上的某一节点作为//sibling赋给task_struct即

pstreea(t,b+1);                                         //实现了向children节点的移//动

                                                                                                                            //现在的问题是 这个中序输出的深度遍历过程是如何实现的,看代码我们可知,这个递归是利用这样的方式完成的:

//A(t){print(t);while(tis not the leave)

//A(t->next);}

//这个while循环很有讲究,其实是一个遍

//历children的循环,但是又处于递归的外//层故该程序是先向下递归再//完成同一层次上的遍历循环

 

}

 

}

 

static int pstree_init(void)

 

{

 

struct task_struct* p;

 

int b=0;

 

for ( p = current; p != &init_task; p =p->parent ) ;//回溯到初始父进程

 

pstreea(p,b);

 

return 0;

 

}

 

static void pstree_exit(void)

 

 

printk("Hello, kernel!/n");   //注意在这儿使用的是printk()函数(不要习惯性地写成printf),//printk()函数是由Linux内核定义的,功能与printf相似。字符//串<1>表示消息的优先级,printk()的一个特点就是它对于不同//优先级的消息进行不同的处理,之所以要在这儿使用高优先级,//是因为默认优先级的消息可能不能显示在控制台上。这个问题//就不详细讲了,可以用man命令寻求帮助。

 

}

 

module_init(pstree_init);

 

module_exit(pstree_exit);//函数init ()和函数exit ( )是模块编程中最基本的也是必须的两个 //函数。init ()                           向内核注册模块所提供//的新功能;exit ()负责注销所有由模块注册的功能