实验介绍
本实验通过编写Linux内核模块,加载内核模块,并测试结果,了解Linux内核模块的概念、编译方法、安装和测试方法。
任务描述
- Linux 内核模块的基本概念
- Linux 内核模块的编写方法
- Linux 内核模块的加载和移除
- Linux内核模块的测试方法
实验目的
- 学习掌握Linux 内核模块的编写
- 学习掌握Linux 内核模块的加载和移除
- 学习掌握测试Linux内核模块
实验内容
1. 掌握Linux 内核模块的基本概念
1.1 解Linux内核模块的基本概念。
1.2 网上查找资料(https://tldp.org/LDP/lkmpg/2.6/html/index.html),了解Linux内核模块的编写方法。
2. 设计和添加Linux 内核模块
2.1 打印“hello,world”
a.切换至home目录(该目录初始应该是空目录)下输入“mkdir hello”新建hello文件,“cd hello”进入hello文件。
b.输入"vim myhello.c",输入以下代码
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
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");
保存并退出(“esc”退出编辑模式,“:wq”保存并退出)
c.输入"vim Makefile",输入以下代码
obj-m :=hello.o
hello-objs:=myhello.o
KDIR :=/lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
default:
make -C $(KDIR) M=$(PWD) modules//make前面为Tab,并不是空格,直接复制需要改下
clean:
make -C $(KDIR) M=$(PWD) clean
保存并退出(“esc”退出编辑模式,“:wq”保存并退出)
d.输入“make”,进行模块编译,如果报错很可能是代码格式问题,请仔细检查,一段代码前不是空格(直接“CV大法”导致的后果)
e.输入“insmod hello.ko”,加载模块
f.输入"dmesg",查看
g.输入“rmmod hello”,卸载模块
2.2 设计一个带参数的模块,其参数为某个进程的PID号,模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID号、进程状态。
a.切换至home目录下输入“mkdir test1”新建test1文件,“cd test1”进入test1文件。
b.输入"vim module1.c",输入以下代码
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
static pid_t pid=1;
module_param(pid, int, 0644);
static int module1_init(void)
{
struct task_struct *p;
struct list_head *pp;
struct task_struct *psibling;
//当前进程的PID
p = pid_task(find_vpid(pid),PIDTYPE_PID);
printk("me: %d %ld %s \n",p->pid,p->state,p->comm);
//父进程
if(p->parent == NULL){
printk("No Parent\n");
}
else{
printk("Parent:%d %ld %s \n",p->parent->pid,p->parent->state,p->parent->comm);
}
//兄弟进程
list_for_each(pp,&p->parent->children)
{
psibling = list_entry(pp,struct task_struct,sibling);
printk("Brother %d %ld %s \n",psibling->pid,psibling->state,psibling->comm);
}
//子进程
list_for_each(pp,&p->children)
{
psibling = list_entry(pp,struct task_struct,sibling);
printk("Children %d %ld %s \n",psibling->pid,psibling->state,psibling->comm);
}
return 0;
}
static void module1_exit(void)
{
printk(KERN_ALERT"goodbye!\n");
}
module_init(module1_init);
module_exit(module1_exit);
MODULE_LICENSE("GPL");
保存并退出(“esc”退出编辑模式,“:wq”保存并退出)
c.输入"vim Makefile",输入以下代码
obj-m := module1.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
保存并退出(“esc”退出编辑模式,“:wq”保存并退出)
d.输入“make”,进行模块编译,如果报错很可能是代码格式问题,请仔细检查,一段代码前不是空格(直接CV导致的后果)
e.输入“insmod module1.ko pid=4”,加载模块
f.输入"dmesg",查看
g.输入“rmmod module1”,卸载模块
2.3 设计一个模块,显示当前系统的名称和版本
a.切换至home目录下输入“mkdir test2”新建test2文件,“cd test2”进入test2文件。
b.输入"vim module2.c",输入以下代码
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/utsname.h>
#include<linux/module.h>
// 初始化函数
static int hello_init(void)
{
printk("sysname:%s \n version:%s",utsname()->sysname,utsname()->version);
return 0;
}
// 清理函数
static void hello_exit(void)
{
printk("goodbye!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
保存并退出(“esc”退出编辑模式,“:wq”保存并退出)
c.输入"vim Makefile",输入以下代码
obj-m := module2.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
保存并退出(“esc”退出编辑模式,“:wq”保存并退出)
d.输入“make”,进行模块编译
e.输入“insmod module2.ko”,加载模块
f.输入"dmesg",查看
这个地方并没有出现我们想要的结果,先别急
g.输入“rmmod module2”,卸载模块,再次输入“dmesg”
卸载模块后结果就出来啦!!!
2.4 设计一个带参数的模块,其参数为指定进程标识符PID和新nice值(优先值),模块功能为修改指定进程的优先级(nice值和prio值)
a.切换至home目录下输入“mkdir test3”新建test3文件,“cd test3”进入test3文件。
b.输入"vim dmodule3.c",输入以下代码
#include<linux/kernel.h>
#include<linux/sched.h>
#include<linux/module.h>
#include<linux/init.h>
#include <linux/moduleparam.h>
MODULE_LICENSE("GPL");
static pid_t pid=1;
static int nice;
module_param(pid,int,0644);
module_param(nice,int,0644);
// 初始化函数
static int hello_init(void)
{
struct task_struct *p;
p = pid_task(find_vpid(pid),PIDTYPE_PID);
printk("当前进程的pid:%d nice:%d ",p->pid,task_nice(p));
if(nice!=0)
{
set_user_nice(p,nice);
printk("当前进程修改后的pid:%d nice:%d ",p->pid,task_nice(p));
}
return 0;
}
// 清理函数
static void hello_exit(void)
{
printk("goodbye!\n");
}
module_init(hello_init);
module_exit(hello_exit);
保存并退出(“esc”退出编辑模式,“:wq”保存并退出)
c.输入"vim Makefile",输入以下代码
obj-m := dmodule3.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
保存并退出(“esc”退出编辑模式,“:wq”保存并退出)
d.输入“make”,进行模块编译
e.输入“ps -l”,查看当前的进程
f.输入“insmod dmodule3.ko pid=2297 nice=5”(根据查看得到的pid值进行相应赋值),加载模块
g.输入"dmesg",查看
这个地方也没有出现我们想要的结果,先别急
h.输入“rmmod dmodule3”,卸载模块,再次输入“dmesg”
i.输入“ps -l”,再次查看,发现nice值已被修改
(PS:截止本篇完成时间为为止,博主还是有些问题没有解决,比如打印名称和版本这个实验,我们将打印放在初始函数中,但是加载模块后,日志中并没有出现信息,名称和版本的信息是在移除模块之后才在日志中出现,修改nice值实验也同样存在这个问题,希望有知道解决办法或其背后原因的小伙伴在评论区评论)
文章写作时间仓促,如有错误请指正,如有问题请在评论区中提问。
加油!