一、实验任务
-
设计一个模块,要求列出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的PID。
-
设计一个带参数的模块,其参数为某个进程的 PID 号,模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID 号及进程状态
二、实验步骤
对于设计一个模块,要求列出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的PID。
新建一个文件夹Operator_System_Exp2_1,将Makefile和show_all_kernel_thread.c文件放在同一个文件夹下。
如果不会在linux虚拟机系统中创建.c文件或者Makefile文件,你就直接在windows中先创建或者直接提供以下链接下载,然后再复制粘贴到虚拟机中。
代码文件链接:通过网盘分享的文件:内核模块编程代码
链接: https://pan.baidu.com/s/1nz0-ZdRSOM2tTBU_6wVAfQ?pwd=a4g8 提取码: a4g8
Makefile文件
obj-m := show_all_kernel_thread.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
show_all_kernel_thread.c文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched/signal.h>
#include <linux/sched.h>
MODULE_LICENSE("GPL");
static int __init show_all_kernel_thread_init(void)
{
struct task_struct *p;
printk("%-20s%-6s%-6s%-6s%-6s", "Name", "PID", "State", "Prio", "PPID");
printk("--------------------------------------------");
for_each_process(p)
{
if (p->mm == NULL)
{
int task_state = task_state_index(p);
printk("%-20s%-6d%-6d%-6d%-6d", p->comm, p->pid, task_state, p->prio,
p->parent->pid);
}
}
return 0;
}
static void __exit show_all_kernel_thread_exit(void)
{
printk("[ShowAllKernelThread] Module Uninstalled.");
}
module_init(show_all_kernel_thread_init);
module_exit(show_all_kernel_thread_exit);
然后用终端方式打开这个文件夹(鼠标放在文件夹上然后鼠标右键)
安装make,gcc功能
sudo apt install make
sudo apt update
sudo apt install buil-essential
编译,添加模块,并输出日志
(1)编译
make
如果先前没有安装make则会报错
(2)添加模块
这一步一定要先进入root模式下才有权限运行,否则会报错
su
insmod show_all_kernel_thread.ko
(3)输出日志查看效果
dmesg
对于设计一个带参数的模块,其参数为某个进程的 PID 号,模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID 号及进程状态。
基本步骤和上面同理
新建一个文件夹Operator_System_Exp2_2,将Makefile和show_task_family.c文件放在同一个文件夹下
代码文件链接:通过网盘分享的文件:内核模块编程代码
链接: https://pan.baidu.com/s/1nz0-ZdRSOM2tTBU_6wVAfQ?pwd=a4g8 提取码: a4g8
Makefile文件
obj-m := show_task_family.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
show_task_family.c文件
// show_task_family.c
// created by 19-03-26
// Arcana
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/pid.h>
#include <linux/list.h>
#include <linux/sched.h>
MODULE_LICENSE("GPL");
static int pid;
module_param(pid, int, 0644);
static int __init show_task_family_init(void)
{
struct pid *ppid;
struct task_struct *p;
struct task_struct *pos;
char *ptype[4] = {"[I]", "[P]", "[S]", "[C]"};
// 通过进程的PID号pid一步步找到进程的进程控制块p
ppid = find_get_pid(pid);
if (ppid == NULL)
{
printk("[ShowTaskFamily] Error, PID not exists.\n");
return -1;
}
p = pid_task(ppid, PIDTYPE_PID);
// 格式化输出表头
printk("%-10s%-20s%-6s%-6s\n", "Type", "Name", "PID", "State");
printk("------------------------------------------\n");
// Itself
// 打印自身信息
int task_state = task_state_index(p);
printk("%-10s%-20s%-6d%-6d\n", ptype[0], p->comm, p->pid, task_state);
// Parent
// 打印父进程信息
int parent_task_state = task_state_index(p->real_parent);
printk("%-10s%-20s%-6d%-6d\n", ptype[1], p->real_parent->comm,
p->real_parent->pid, parent_task_state);
// Siblings
// 遍历父进程的子,即我的兄弟进程,输出信息
// “我”同样是父进程的子进程,所以当二者进程PID号一致时,跳过不输出
list_for_each_entry(pos, &(p->real_parent->children), sibling)
{
if (pos->pid == pid)
continue;
int sibling_task_state = task_state_index(pos);
printk("%-10s%-20s%-6d%-6d\n", ptype[2], pos->comm, pos->pid,
sibling_task_state);
}
// Children
// 遍历”我“的子进程,输出信息
list_for_each_entry(pos, &(p->children), sibling)
{
int child_task_state = task_state_index(pos);
printk("%-10s%-20s%-6d%-6d\n", ptype[3], pos->comm, pos->pid,
child_task_state);
}
return 0;
}
static void __exit show_task_family_exit(void)
{
printk("[ShowTaskFamily] Module Uninstalled.\n");
}
module_init(show_task_family_init);
module_exit(show_task_family_exit);
然后用终端方式打开这个文件夹
编译,添加模块,并输出日志
(1)编译
make
(2)添加模块
su
insmod show_task_family.ko pid=10
注意注意注意!
pid=xxx这个进程号需要到第一个设计实验的查看结果那里看!
(3)输出日志查看效果
dmesg