在本章实验中问题一的tasklist模块逐一读取系统中各个进程task_struct的相关信息并显示出来,现须一次性地读取和显示系统中的所有进程的task_struct的相关信息,
在问题一的基础上,改写my_seq_show使其一次读取所有进程的task_struct的相关信息,通过使用for_each_process遍历系统中的所有进程。
5.2概要设计
实现本次实验的文件为安装内核模块的Makefile文件,以及改写的tasklist文件,其中Makefile文件的内容与前几次实验相同。
对于tasklist.c文件,除了包含my_exit,my_init等常规内核模块应有的函数,还包括proc文件操作函数集合const struct proc_ops my_proc,以及my_seq_show用于获取进程的相关信息(进程pid,进程状态以及进程名称)。
5.3详细设计
模块my_seq_show,需要传入序列文件接口seq_file( 这个接口提供了一套简单的函数便于proc接口编程时解决问题)该函数中使用for_each_process遍历task_struct,并调用seq_printf输出进程的相关信息。
对于my_open函数,其为自定义的proc文件操作函数my_proc中的open函数,用于返回打开序列文件并关联my_seq_show。
my_proc定义了proc文件操作函数集合(open,read,lseek,以及release)
my_init模块主要通过proc_create创建proc伪文件。
my_exit模块完成对proc伪文件的删除。
实验过程如下:
在实验目录下make编译tasklist.c文件
安装tasklist.ko模块,使用cat命令查看/proc/tasklist的内容,将内容重定向至文本文件中便于查看。
实验输出为(截取部分)
5.4调试分析
分析一:proc_create第四个参数匹配错误
第一个解决方案是在5.6或更高版本的内核中用struct proc_ops替换struct file_operations。第二种解决方案是,对于内核版本4.18或更高版本,当所需的只是一个基本的、只读的proc文件时,用proc_create_single替换proc_create。
分析二:不兼容的指针类型
在对tasklist.c文件改写时,my_seq_show(struct seq_file *m, void *v)的传入参数有个void*指针,函数中却并没有用到这个指针,删除后编译时依然报错指针类型不兼容,
由编译器的note提示可知,single_open的第二个参数为int(*)(struct seq_file *, void*)那这个void*在show函数中作用如下:show方法就是负责将v指向元素中的数据输出到seq_file的内部缓存,但是其中必须借助seq_file提供的一些类似printf的接口函数(即函数中的seq_printf函数)
即是说void*在seq_file中的show方法是有用的,而在改写的my_seq_show中,虽然没有用到该指针,但在声明函数时依然不可或缺
takelist.c
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
char modname[] = "tasklist";
声明要创建的proc文件的名称,此处为“/proc/tasklist”
struct task_struct *task;
int taskcounts=0; // 'global' so value will be retained
//获取进程的相关信息
static int my_seq_show(struct seq_file *m, void *v)
{
for_each_process(task) {
seq_printf( m, "#%-3d ", taskcounts++ );
seq_printf( m, "%5d ", task->pid );//进程pid(标识符)
seq_printf( m, "%lu ", task->state );//进程状态
seq_printf( m, "%-15s ", task->comm );//进程名称
seq_puts( m, "\n" );
}
return 0;
}
//在定义的proc文件操作函数my_proc中的open函数中指明使用该结构体
static int my_open(struct inode *inode, struct file *file)
{
return single_open(file, my_seq_show,NULL); //打开序列文件并关联my_seq_show
}
//先定义proc文件操作函数结构体,并实现自己定义的函数,如my_open
//注意 file_operations my_proc 是较老的版本,如果依此定义文件操作函数结构体
//会出现指针不匹配的问题,有两个解决方案
//第一个解决方案是在5.6或更高版本的内核中用struct proc_ops替换struct file_operations:
//第二种解决方案是,对于内核版本4.18或更高版本,当所需的只是一个基本的、只读的proc文件时,
//用proc_create_single替换proc_create:
static const struct proc_ops my_proc =
{ //proc文件操作函数集合
.proc_open = my_open,//open操作由自定义的my_open函数实现
.proc_read = seq_read,//read操作由系统的seq_read来完成
.proc_lseek = seq_lseek,//llseek操作由系统的seq_llseek来完成
.proc_release = seq_release//release操作由系统的seq_resease来完成
};
int __init my_init( void )
{
struct proc_dir_entry* my_proc_entry;
printk( "<1>\for_each_processnInstalling \'%s\' module\n", modname );
my_proc_entry = proc_create(modname, 0x644, NULL, &my_proc);//生成proc文件
if (NULL == my_proc_entry)
{
return -ENOMEM;
}
return 0; //SUCCESS
}
void __exit my_exit( void )
{
remove_proc_entry( modname, NULL );//删除proc文件
printk( "<1>\nRemoving \'%s\' module\n", modname );
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
makefile
obj-m:=tasklist.o
KDIR:= /lib/modules/$(shell uname -r)/build
PWD:= $(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -r -f .tmp_versions *.mod.c .*.cmd *.o *.symvers