创建显示系统进程信息的 proc 模块
Linux遵循现代操作的普遍原则:使程序员和内核、硬件等系统资源隔离开,普通用户无法看到内核空间中发生了什么,系统调用是操作系统提供给应用程序使用操作系统服务的重要接口,但同时也屏蔽了用户直接访问操作系统内核的可能性。Linux 提供了 LKM 机制可以使我们在内核空间工作。Linux 提供的LKM 机制中一个重要的组成部分就是proc 伪文件系统。
Proc 包含了当前计算机中重要的资源信息及系统信息。
我们要实现一个内核模块,该模块创建/proc/tasklist 文件,并且提取系统中所有进程的 pid、state和名称进行显示。
总的流程 :
- 首先可以先看一下系统中 proc 目录下是否有 tasklist 这个文件夹
- 运行 make 进行编译
- 内核模块添加 $sudo insmod tasklist.ko
- 添加内核模块后读取并信息 tasklist 内核信息: $ cat /proc/tasklist
(1)首先可以先看一下系统中 proc 目录下是否有 tasklist 这个文件夹
ls -a
/proc目录下没有tasklist文件夹
(2)新建文件夹task3_1,编写函数tasklist.c和Makefile文件,并编译,代码在最后
(3)安装模块tasklist.ko
sudo insmod tasklist.ko
(4)显示进程信息cat /proc/tasklist
cat /proc/tasklist
// 编译命令: make
// 内核模块添加:$sudo insmod tasklist.ko
// 添加内核模块后读取并信息tasklist内核信息: $ cat /proc/tasklist
// 内核模块删除:$sudo rmmod tasklist
Makefile文件
ifneq ($(KERNELRELEASE),)
obj-m := tasklist.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
rm -r -f .tmp_versions *.mod.c .*.cmd *.o *.symvers
endif
tasklist.c
#include <linux/module.h> // 初始化模块
#include <linux/proc_fs.h> // 创建进程信息入口
#include <linux/sched/task.h> // 初始进程
#include <linux/seq_file.h> // 序列文件
#include <linux/slab.h> // 内存分配释放
#include <linux/sched/signal.h> //下一个进程
char modname[] = "tasklist";
struct task_struct *task;
int taskcounts=0; // 全局进程变量
static void * my_seq_start(struct seq_file *m, loff_t *pos)
{
///printk(KERN_INFO"Invoke start\n"); //可以输出调试信息
if ( *pos == 0 ) // 表示遍历开始
{
task = &init_task; //遍历开始的记录地址
return &task; //返回一个非零值表示开始遍历
}
else //遍历过程中
{
if (task == &init_task ) //重新回到初始地址,退出
return NULL;
return (void*)pos ;//否则返回一个非零值
}
}
static int my_seq_show(struct seq_file *m, void *v)
{//获取进程的相关信息
//printk(KERN_INFO"Invoke show\n");
seq_printf( m, "#%-3d\t ", taskcounts ); //输出进程序号
seq_printf( m, "%d\t", task->pid ); //输出进程pid
seq_printf( m, "%lu\t ", task->state ); //输出进程state
seq_printf( m, "%s\t ", task->comm ); //输出进程名称(comm)
seq_puts( m, "\n" );
return 0;
}
static void * my_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
//printk(KERN_INFO"Invoke next\n");
(*pos)++;
//task指向下一个进程?
taskcounts++;
task= next_task(task); //指向下一个进程
return NULL;
}
static void my_seq_stop(struct seq_file *m, void *v)
{
//printk(KERN_INFO"Invoke stop\n");
// do nothing
}
static struct seq_operations my_seq_fops = {//序列文件记录操作函数集合
.start = my_seq_start,
.next = my_seq_next,
.stop = my_seq_stop,
.show = my_seq_show
};
static int my_open(struct inode *inode, struct file *file)
{
return seq_open(file, &my_seq_fops); //打开序列文件并关联my_seq_fops
}
static const struct file_operations my_proc =
{ //proc文件操作函数集合
.owner = THIS_MODULE,
.open = my_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
int __init my_init( void )
{
struct proc_dir_entry* my_proc_entry;
printk( "<1>\nInstalling \'%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>Removing \'%s\' module\n", modname );
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
欢迎批评指正!