第一个内核模块实验
注:本实验基于Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-93-generic x86_64)
实验环境为阿里云服务器轻量级应用服务器
一. 、预习要求
(1)做本实验之前,请复习PPT里内核模块相关知识。
(2)请了解内核模块的编写、编译及安装与卸载方法。
(3)请学习内核打印函数printk()的用法。
(4)请复习Makefile文件的编写。
二、实验目的
(1)掌握内核模块的组成部分。
(2)掌握内核模块的编译、安装与卸载方法。(3)学会查看内核模块的信息。
三、实验要求
(1)编写三个模块mainmod、lenmod、summod。summod模块导出对数组元素求和函数,lenmod模块导出求数组元素的个数函数,mainmod模块使用summod和lenmod导出的函数。注意路径清晰,分别在三个目录下编写。
(2)每个shell命令提示符的用户名必须是自己的名字。
四、实验步骤
1.建立3个文件夹mainmod、lenmod、summod
2.分别编写mainmod.c、lenmod.c、summod.c以及各自文件夹的Makefile
mainmod.c
lenmod.c
summod.c
3.4.按summod、lenmod、mainmod的顺序编译(make)并插入模块
5.使用dmesg |tail -10查看结果
五、实验代码
mainmod.c
/*包含了对模块的结构定义以及模块的版本控制,
* 任何模块程序的编写都要包含这个头文件*/
#include <linux/module.h>
#include <linux/kernel.h> //包含了常用的内核函数
extern int sum(int* a,int len);
extern int len(int* a);
/*宏__init告诉编译程序相关的函数和变量仅用于初始化,
* 编译程序将标有__init的所有代码存储到特殊的内存段中,
* 初始化结束后就释放这段内存*/
#include <linux/init.h> //包含了宏__init和宏__exit
static int __init lkp_init(void) //lkp_init()是模块初始化函数
{
/*printk()函数,该函数是由内核定义的,功能与C库中的
printf()类似,它把要打印的信息输出到终端或者系统日志*/
int result = 0;
int length = 0;
int a[] = {1,6,4,9};
printk(KERN_INFO"Hello,mainmod!\n");
length = len(a);
result = sum(a,length);
printk(KERN_INFO"Array's length is %d,Array's sum is %d\n",length,result);
return 0;
}
static void __exit lkp_cleanup(void) //lkp_cleanup()是模块的退出和清理函数
{
printk("<1>Goodbye,World! leaving kernel space...\n");
}
/*是模块编程中最基本也是必需的两个函数*/
module_init(lkp_init); //向内核注册模块提供新功能
module_exit(lkp_cleanup); //注销由模块提供所有的功能
MODULE_LICENSE("GPL"); //告诉内核该模块具有GNU公共许可证
Mainmod的Makefile
obj-m := mainmod.o
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
KBUILD_EXTRA_SYMBOLS += /root/summod/Module.symvers
KBUILD_EXTRA_SYMBOLS += /root/lenmod/Module.symvers
export KBUILD_EXTRA_SYMBOLS
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -f *.o *.ko *.mod.c *.order *.symvers
lenmod.c
/*包含了对模块的结构定义以及模块的版本控制,
* 任何模块程序的编写都要包含这个头文件*/
#include <linux/module.h>
#include <linux/kernel.h> //包含了常用的内核函数
/*宏__init告诉编译程序相关的函数和变量仅用于初始化,
* 编译程序将标有__init的所有代码存储到特殊的内存段中,
* 初始化结束后就释放这段内存*/
#include <linux/init.h> //包含了宏__init和宏__exit
static int __init lkp_init(void) //lkp_init()是模块初始化函数
{
/*printk()函数,该函数是由内核定义的,功能与C库中的
printf()类似,它把要打印的信息输出到终端或者系统日志*/
printk(KERN_INFO"Hello,lenmod!\n");
return 0;
}
static void __exit lkp_cleanup(void) //lkp_cleanup()是模块的退出和清理函数
{
printk("<1>Goodbye,World! leaving kernel space...\n");
}
int len(int* a){
printk(KERN_INFO"Hello,lenmod!sizeof=%d\n",(int)sizeof(a));
return (int)sizeof(a)/2;
}
EXPORT_SYMBOL(len);
/*是模块编程中最基本也是必需的两个函数*/
module_init(lkp_init); //向内核注册模块提供新功能
module_exit(lkp_cleanup); //注销由模块提供所有的功能
MODULE_LICENSE("GPL"); //告诉内核该模块具有GNU公共许可证
Lenmod的Makefile
obj-m :=lenmod.o
all:
make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) modules #编译模块
clean:
make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) clean #清理
summod.c
/*包含了对模块的结构定义以及模块的版本控制,
* 任何模块程序的编写都要包含这个头文件*/
#include <linux/module.h>
#include <linux/kernel.h> //包含了常用的内核函数
/*宏__init告诉编译程序相关的函数和变量仅用于初始化,
* 编译程序将标有__init的所有代码存储到特殊的内存段中,
* 初始化结束后就释放这段内存*/
#include <linux/init.h> //包含了宏__init和宏__exit
static int __init lkp_init(void) //lkp_init()是模块初始化函数
{
/*printk()函数,该函数是由内核定义的,功能与C库中的
printf()类似,它把要打印的信息输出到终端或者系统日志*/
printk(KERN_INFO"Hello,summod!\n");
return 0;
}
static void __exit lkp_cleanup(void) //lkp_cleanup()是模块的退出和清理函数
{
printk("<1>Goodbye,World! leaving kernel space...\n");
}
int sum(int* a,int len)
{
int i = 0;
int result = 0;
for(i = 0;i < len;i++)
{
result += a[i];
}
return result;
}
EXPORT_SYMBOL(sum);
/*是模块编程中最基本也是必需的两个函数*/
module_init(lkp_init); //向内核注册模块提供新功能
module_exit(lkp_cleanup); //注销由模块提供所有的功能
MODULE_LICENSE("GPL"); //告诉内核该模块具有GNU公共许可证
Summod的Makefile
obj-m :=summod.o
all:
make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) modules #编译模块
clean:
make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) clean #清理
六、实验总结
1.在mainmod中,要加入
extern int sum(int* a,int len);
extern int len(int* a);
来引入外部函数
2.在被依赖的函数后面,要使用
EXPORT_SYMBOL(len);
来进行导入函数
3.在mainmod的Makefile文件中,要加入
KBUILD_EXTRA_SYMBOLS += /root/summod/Module.symvers
KBUILD_EXTRA_SYMBOLS += /root/lenmod/Module.symvers
export KBUILD_EXTRA_SYMBOLS
来引入其他模块的Module.symvers
参考文献
https://blog.csdn.net/ningxiaowei2013/article/details/13630267
https://blog.csdn.net/xhz1234/article/details/44278137 Copyright 徐洪志(MacroSAN). All rights reserved.
https://blog.csdn.net/weixin_43858015/article/details/106209813