【OS】操作系统-实验二:模块的编写
操作环境:
- 主机:MacOS 10.13.3
- 虚拟机软件:Parallels Desktop
- Linux:Ubuntu 16.04 Kernal 4.13.0-36-generic
实验要求:
设计一个模块,要求列出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的 PID。
设计一个带参数的模块,其参数为某个进程的 PID 号,模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID 号、进程状态。
请根据自身情况,进一步阅读分析程序中用到的相关内核函数的源码实现。
操作说明:
列出系统中所有内核线程模块设计:
- 编写模块,命名为
aeonni_ps.c
代码见附录 - 编写
Makefile
文件,内容及文件如下:
$ make
编译内核:
- 测试模块:(加载,卸载,查看,清除一条龙)
$ sudo insmod aeonni_ps.ko && sudo rmmod aeonni_ps && sudo dmesg -c
- 编写模块,命名为
带参数的模块设计:
- 编写模块,命名为
aeonni_pid.c
代码见附录 - 编写
Makefile
文件,内容及文件如下:
$ make
编译内核$ ps -ef
命令或直接在上个模块的输出中选择一个顺眼的 PID,这里选择2
测试模块:(加载,卸载,查看,清除一条龙)
$ sudo insmod aeonni_pid.ko pid=2 && sudo rmmod aeonni_pid && sudo dmesg -c
- 得到结果
- 编写模块,命名为
源码分析:
列出系统中所有内核线程模块设计:
- 要列出所有进程数据,只需要使用定义在
/include/linux/sched.h
中的for_each_process()
传入一个指向task_struct
的指针,直接通过printk
打印即可。 task_struct
中的有关成员为- task->comm --- 进程名称 - char[]
- task->pid ---- PID - pid_t
- task->state -- 状态 - volatile long
- task->prio --- 优先级 - int
- task->parent - 指向父进程的指针 - task_struct *
- 要列出所有进程数据,只需要使用定义在
带参数的模块设计:
参数的传入:
linux/moduleparam.h
中定义了有关参数传递的函数,使用如下语句创建传入的参数。
static int pid; module_param(pid, int, 0644); MODULE_PARM_DESC(pid, "An int variable");
- 最终可以直接在
insmod
命令后加上pid=x
传入参数值
task_struct
中的有关成员为- task->sibling - list_head
- task->children - list_head
sibling.next
指向进程的下一个兄弟进程的进程描述符 sibling 成员,若其后没有其他兄弟进程,则指向父进程;而 sibling.prev 指向进程的上一个兄弟进程,若其之前没有兄弟进程,则指向父进程。children.next
指向父进程的第一个子进程的 sibling 成员(而不是children成员!),而 children.prev 却指向父进程的最后一个子进程的 sibling 成员。- 特别注意 children.next 指向的是 sibling 成员,因此在使用 list_entry() 获得 task_struct 指针时,参数要用 sibling 而不是 children。
使用
find_get_pid()
函数,根据传入的 pid 值获取到对应进程的指针 task,使用list_for_each(pos, head)
遍历链表,并使用list_entry()
获取 task_struct 指针,最后重复上一个模块的输出操作。
附录:
- 模块代码
aeonni_ps.c
:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
static int aeonni_ps_init(void)
{
struct task_struct * task;
for_each_process(task)
{
//程序名、PID、进程状态、进程优先级、父进程的 PID
if(task->mm == NULL) {
printk("%s { \n\
\tPID: %d, \n\
\tState: %d, \n\
\tPriority: %d, \n\
\tP_PID: %d \n\
}",task->comm,task->pid, task->state, task->prio, task->parent->pid);
}
}
return 0;
}
static void aeonni_ps_exit(void)
{
printk(KERN_ALERT"goodbye\n");
}
module_init(aeonni_ps_init);
module_exit(aeonni_ps_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Genius.AEONNI.COM");
MODULE_DESCRIPTION("This module is for the first demand of Exercise3.");
- 模块代码
aeonni_pid.c
:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/moduleparam.h>
static int pid;
module_param(pid, int, 0644);
MODULE_PARM_DESC(pid, "An int variable");
static int aeonni_pid_init(void)
{
struct pid * kpid;
struct task_struct * task, * sib;
struct list_head * list;
kpid = find_get_pid(pid);/* 返回pid */
task = pid_task(kpid, PIDTYPE_PID);/* 返回task_struct */
//包括父进程、兄弟进程和子进程的程序名、PID 号、进程状态。
printk(
"\n%s: { \n\
PID: %d, \n\
State: %d, \n\
Priority: %d, \n\
Parent: %s { \n\
PID: %d, \n\
State: %d, \n\
} \n\
Siblings: [\n",task->comm,task->pid, task->state, task->prio, task->parent->comm, task->parent->pid, task->parent->state);
// 列出兄弟进程
list_for_each(list, &(task->sibling)){
sib = list_entry(list, struct task_struct, sibling);
printk(
" %s: { \n\
PID: %d, \n\
State: %d, \n\
}\n",sib->comm,sib->pid, sib->state);
}
// 列出子进程
printk(" ], \n\
Children: [\n");
list_for_each(list, &(task->children)){
sib = list_entry(list, struct task_struct, sibling);
printk(
" %s: { \n\
PID: %d, \n\
State: %d, \n\
}\n",sib->comm,sib->pid, sib->state);
}
printk(" ]\n}");
return 0;
}
static void aeonni_pid_exit(void)
{
printk(KERN_ALERT"Goodbye_(┐「ε:)_\n");
}
module_init(aeonni_pid_init);
module_exit(aeonni_pid_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Genius.AEONNI.COM");
MODULE_DESCRIPTION("This module is for the second demand of Exercise3.");