如何获取Linux所有进程信息

描述

很多时候,我们要监控系统状态,即监控系统cpu负载、进程状态等情况,如果我们在 Linux 应用层,我们有很多方式,命令行中常用 top、ps 命令,代码中,我们可以使用 popen 函数去执行一个 top 命令,获取返回值。或者我们直接读写 /proc下面的文件,都可以达到目的。

但如果要你在内核(驱动)中去获取这些信息,你无法执行 top 命令。即便可以使用 flip_open 函数和加 vfs_read 内核函数去读写 /proc 节点文件,但 Linux 本身不建议这样做,这会破坏节点(驱动)之间的独立性,如果产生依赖关系,很可能产生各种各样的问题:如果你在一个节点驱动中读写另外一个节点的内容,而另外一个挂载出了问题,系统就会不稳定。

所以我们一般直接从 Linux 内核本身获取信息,去分析 Linux 内核源码,从他本身的数据结构(结构体、变量、链表)中获取信息。

今天教大家如何在驱动中直接获取 linux 系统中所有进程信息。进程有很多信息,在 /proc/[pid] 目录中中只放了很少一部分,我们访问内核数据结构可以获取全部所有信息。

我们知道Linux系统管理进程是使用PCB(process control block),进程控制块,内核使用一个结构体描述它,这个结构体现在有600多行,叫 task_struct 结构体,这个结构体在 linux 内核源码 linux/include/linux/sched.h 中。

task.c

# include < linux/kernel.h >
# include < linux/module.h >
# include < uapi/linux/sched.h >
# include < linux/init_task.h >
# include < linux/init.h >
# include < linux/fdtable.h >
# include < linux/fs_struct.h >
# include < linux/mm_types.h >
//内核模块初始化函数
static int __init traverse_pcb(void)
{
 struct task_struct *task, *p;//定义指向task_struct类型的指针
 struct list_head *pos;//定义双向链表指针
 int count=0;//定义统计系统进程个数的变量
 printk("Printf process'message begin:n");//提示模块开始运行
 task = &init_task;//指向0号进程的PCB
 
 list_for_each(pos,&task- >tasks)//使用list_for_each宏来遍历进程链表
 {
  p = list_entry(pos,struct task_struct,tasks);//指向当前进程的task_struct结构
  count++;//统计系统进程个数
  printk("nn");//方便查看后续打印信息
  /*
  打印task_struct中的字段.comm:name.pid:进程的pid号;state:进程的状态;
  prio:动态优先级;static_prio:静态优先级; parent'pid:父进程的pid号;
  count:文件系统信息,文件被使用的次数; umask:进程权限位的默认设置;
  使用atomic_read原子操作是为了(p- >files)- >count字段计数不被打断
  */
  printk("comm:%s; pid:%d; state:%lx; prio:%d; static_prio:%d; parent'pid:%d; count:%d; umask:%d;", 
   p- >comm,p- >pid,p- >state,p- >prio,p- >static_prio,(p- >parent)- >pid,        
   atomic_read((&(p- >files)- >count)),(p- >fs)- >umask);
  //打印进程地址空间的信息
  if((p- >mm)!=NULL)
   printk("total_vm:%ld;",(p- >mm)- >total_vm);//total_vm:线性区总的页数
 }
 printk("进程的个数:%dn",count);//打印进程个数
 return 0;
}
 
//内核模块退出函数
static void __exit end_pcb(void)
{
 printk("traverse pcb is end.");
}
module_init(traverse_pcb);//入口
module_exit(end_pcb);//出口
MODULE_LICENSE("GPL");//许可证

Makefile

KERNELDIR := /home/book/linux/tool/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

CURRENT_PATH := $(shell pwd)

obj-m := task.o

build: kernel_modules

kernel_modules:
 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

编译

监控系统

我们会编译出一个task.ko内核模块,把它拷贝到板子中,进行挂载。

监控系统

挂载上以后就会触发 _init 函数,就会打印。当然我们也可以把这段代码放在 xxx_read 函数中,在 _init 函数中创建节点,访问一次节点就打印一次,都可以。

博主这个系统进程很少,只有 70-80 个,是 buildroot 自己编译的文件系统,没有什么功能。

我们通过定义的p结构体指针,可以访问系统中所有进程的 io、运行时间、内存信息、进程被调用次数,任何和进程有关的信息都存在于 task_struct 中。

Linux 内核采用 task_struct 来描述一个进程。当系统起来以后,随着 init(pid=1)进程 fork 出其他进程,会有一个双向链表,将所有的由 init 创建的进程串起来,我们通过遍历这个双向链表,进而获取所有进程的 task_struct 结构体,把信息取出来。在驱动中这样做,远比访问 /proc 文件方便多了。

note:编译之前记得准备好你的 Linux 内核源码,因为编译需要引用头文件,所以我们在 Makefile 中写明 Linux 内核源码目录(源码必须是编译过的源码,编译 Linux 大概需要半个多小时)。另外需要注意,你编译驱动所引用的内核和你板子中真正运行的 Linux 内核要需要是同一个版本,否则挂载不上去。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分