题目内容:
(1)当模块被载入内核时会向系统日志文件中写入“hello,world”;当被卸载时,会向系统写入“goodbye”。
(2)设计一个模块,要求列出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的PID。
(3)设计一个带参数的模块,其参数为某个进程的PID号,模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID号及进程状态。
一开始使用for_each_process(p)来找到与用户输入pid相匹配的进程,并且通过判断state是否为空来判断pid是否存在,虽然最后结果正确,但是make的时候对p->state==NULL中的”==”报错
使用实验一中pid_task(find_get_pid(pid),PIDTYPE_PID);来找到task_struct,然后通过判断p是否是否指向空来判断pid是否存在。
在home文件夹中创建hello文件夹,新建myhello.c和Makefile
myhello.c
#include
#include
#include
static int hello_init(void)
{
printk(KERN_ALERT"hello,word\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT"goodbye\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
Makefile
obj-m :=hello.o
hello-objs:=myhello.o
KDIR :=/lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
default:
/*-C指定内核源码目录,M指定模块源码所在目录*/
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
进入hello文件夹,输入make
输入sudo insmod ./hello.ko加载模块命令
输入lsmod查看模块是否被编译
输入dmesg查看是否在日志输出
输入sudo rmmod hello卸载模块
输入dmesg查看结果
在home文件中新建process文件夹,新建module1.c和Makefile文件
module1.c
#include
#include
#include
#include
#include
static int hello_init(void)
{
struct task_struct *p;
/*进程描述符指针p*/
p=&init_task;
/*p指向init_task
内核中init_task变量就是是进程0(调度程序)使用的进程描述符,也是Linux系统中第一个进程描述符,贯穿于整个Linux系统的初始化过程,所以不可能不存在*/
printk(KERN_ALERT"名称\t进程号\t状态\t优先级\t父进程号\t");
/*
#define KERN_EMERG KERN_SOH "0" /*系统无法使用*/
#define KERN_ALERT KERN_SOH "1" /*必须马上输出*/
#define KERN_CRIT KERN_SOH "2" /*临界*/
#define KERN_ERR KERN_SOH "3" /*错误*/
#define KERN_WARNING KERN_SOH "4" /*警告*/
#define KERN_NOTICE KERN_SOH "5" /*正常但重要*/
#define KERN_INFO KERN_SOH "6" /*信息*/
#define KERN_DEBUG KERN_SOH "7" /*调试级别信息*/
*/
for_each_process(p)
/* for_each_process是宏循环控制语句,内核开发者可它扫描整个进程链表。
#define for_each_process(p) \
for (p = &init_task ; (p = next_task(p)) != &init_task ; )
一直循环下去,直到再次碰上init_task为止,因为循环链表*/
{
if(p->mm == NULL){
/* mm是指向被映射的用户地址空间的内存管理结构的指针
struct mm_struct *mm, *active_mm;
#ifdef CONFIG_COMPAT_BRK
unsigned brk_randomized:1;
#endif
#if defined(SPLIT_RSS_COUNTING)
struct task_rss_stat rss_stat;
#endif
内核进程始终在内核空间运行,从来不切换到用户空间去,所以没有用户态地址空间,所以它们的mm成员总是为NULL*/
printk(KERN_ALERT"%s\t%d\t%ld\t%d\n",p->comm,p->pid, p->state,p->normal_prio,p->parent->pid);
/*
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
int prio, static_prio, normal_prio;
pid_t pid;
char comm[TASK_COMM_LEN]; /* executable name excluding path */
…};
相应的程序名char common[TASK_COMM_LEN]
进程标识符pid_t pid
进程状态volatile long state
常规静态优先级int normal_prio
*/
}
}
return 0;
}
// 清理函数
static void hello_exit(void)
{
printk(KERN_ALERT"goodbye~\n");
}
// 函数注册
module_init(hello_init);
module_exit(hello_exit);
// 模块许可申明
MODULE_LICENSE("GPL");
Makefile
obj-m:=module1.o
KDIR:= /lib/modules/$(shell uname -r)/build
PWD:= $(shell pwd)
all:default test
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
@sudo rmmod module1
$(MAKE) -C $(KDIR) M=$(PWD) clean
@dmesg | tail -10
test:
/*清除原有信息*/
@sudo dmesg -C
@sudo insmod module1.ko
@dmesg
在cd process进入process文件夹
输入make
检验:输入ps -u root查看root用户的进程
经检查与结果一致
输入make clean
#include
#include
#include
#include
#include
#include
/*在使用宏之前,先声明无符号变量*/
static unsigned int pid;
/*module_param(name,type,perm)
name既是用户看到的参数名,有事模块内接收参数的变量
type表示数据的类型,例如:int,double,bool,unit...
perm对于全局可读的变量为0444,对于根目录可写为0644。
*/
module_param(pid, uint, 0644);
static int list_init(void)
{
/*parent 指向其父进程
children 表示当前进程的子进程
sibling 表示当前进程的链表*/
/*定义类型为结构体的指针*/
struct task_struct *p;
struct task_struct *parent;
struct task_struct *children;
struct task_struct *sibling;
struct list_head *list;
p=NULL;
/*根据进程号pid得到进程描述符struct pid
struct pid *find_get_pid(pid_t nr)
{
struct pid *pid;
rcu_read_lock();
/*读端临界区,阻塞写者*/
pid = get_pid(find_vpid(nr));
/*find_vpid(nr)根据nr也就是namespace下的局部pid找到对应的struct pid结构体
get_pid 将struct pid结构体中的count字段(记录被使用的次数)加1 后,返回struct pid*/
rcu_read_unlock();
/*写者可以进行下一步操作*/
return pid;
}
pid_task(struct pid *pid, enum pid_type type) PIDTYPE_PID是enum pid_type type中进程的进程号,返回进程的task_struct */
p=pid_task(find_get_pid(pid),PIDTYPE_PID);
if(p==NULL)
printk(KERN_ALERT"Pid does not exit\n");
else{
parent=p->parent;
printk(KERN_ALERT"This is parent:\n");
printk(KERN_ALERT"程序名\t\tPID号\t进程状态\t优先级\n");
printk(KERN_ALERT"%-10s\t%5d\t%ld\t\t%d\n",parent->comm, parent->pid, parent->state, parent->prio);
/*
sibling.next指向进程的下一个兄弟进程的进程描述符sibling成员,若其后没有其他兄弟进程,则指向父进程
sibling.prev指向进程的上一个兄弟进程,若其之前没有兄弟进程,则指向父进程
children.next指向父进程的第一个子进程的sibling成员(而不是children成员!)
children.prev指向父进程的最后一个子进程的sibling成员。
*/
printk(KERN_ALERT"This is sibling:");
printk(KERN_ALERT"程序名\t\tPID号\t进程状态\t优先级\n");
/*遍历兄弟进程*/
/*#define list_for_each(pos,head)\
for(pos=(head)->next;pos!=(head);pos=pos->next)
pos是指向list_head的指针,用来具体地对每一个list_head进行操作*/
list_for_each(list,&parent->children)
{
/*
#define list_entry(ptr,type,member)\
container_of(ptr,type,member)
#define container_of(ptr,type,member)({\
const typeof( ((type *)0)->member)*_mptr=(ptr);
(type *)( (char *)_mptr - offsetof(type,member));}
offsetof获得该成员变量基于其包含体地址的偏移量
ptr指向该数据存储在链表中的地址值
type是数据项的类型
member是数据项类型定义中list_head成员的变量名
计算tpye结构体中成员member在结构体中的偏移量,然后用ptr的值减去这个偏移量,就得出type数据结构的首地址*/
sibling=list_entry(list,struct task_struct,sibling);
printk(KERN_ALERT"%-10s\t%5d\t%ld\t\t%d\n", sibling->comm, sibling->pid, sibling->state, sibling->prio);
}
printk(KERN_ALERT"This is children:\n");
printk(KERN_ALERT"程序名\t\tPID号\t进程状态\t优先级\n");
/*遍历子进程*/
list_for_each(list,&p->children)
{
children=list_entry(list,struct task_struct,sibling);
printk(KERN_ALERT"%-10s\t%5d\t%ld\t\t%d\n", children->comm, children->pid, children->state, children->prio);
}
}
return 0;
}
static void list_exit(void)
{
printk(KERN_ALERT"goodbye~\n");
}
module_init(list_init);
module_exit(list_exit);
MODULE_LICENSE("GPL");
Makefile
obj-m := list.o
KDIR := /lib/modules/`uname -r`/build
PWD := $(shell pwd)
default:
sudo make -C $(KDIR) M=$(PWD) modules
@sudo dmesg -C
clean:
- sudo rmmod list
sudo make -C $(KDIR) M=$(PWD) clean
pid不存在
通过遍历所有进程for_each_process(p)检查每个进程的pid是否和用户输入的pid相同,若找到pid相同的进程则输出此进程的父进程、兄弟进程、子进程,然后退出遍历。
所有进程都是pid为1的init进程的后代。
结构体list_head包含两个指针成员:next,prev。这两个指针成员都是list_head类型,以此构成链表。实际应用中,list_head结构体往往实例化为其他结构体的成员,例如task_struct中的children,sibling。