文章目录
一、Linux内核模块命令二、内核模块代码分析1.头文件2.模块初始化和退出函数3.模块参数的函数(1)宏描述(2)一般变量(3)字符串(4)数组
4.学术规范
三、实验1.任务2.过程(1)编写内核模块.c文件(2)编写Makefile文件(3)编译(4)安装模块:(5)查看`printk`的输出在缓冲区的信息:(6)卸载模块:(7)清除`printk`输出在缓存区的信息:
四、问题1.for循环初始化声明分开2.先卸载残留才能安装3.震惊!printk居然不能输出%c
一、Linux内核模块命令
内核模块的安装(加载): sudo insmod module_name.ko 如果有参数的话就是加上param_name=value:
int型:year=2020字符串:direction=embedded_system。 注意:都不行direction="embedded_system"和direction=embedded system和direction=embedded\ system数组:date_array=2020,3,9,用逗号分隔
内核模块的卸载: sudo rmmod module_name或modprobe -r module_name
查看系统已经加载的模块: lsmod
查看系统已经加载的模块信息: modinfo
查看printk输出在缓存区的信息: dmesg
清除printk输出在缓存区的信息: sudo dmesg -c
二、内核模块代码分析
1.头文件
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
2.模块初始化和退出函数
static int __init
hello_init(void)
{
return 0;
}
static void __exit
hello_exit(void)
{
}
module_init(hello_init
);
module_exit(hello_exit
);
module_init()和module_exit()内填的是具体实现初始化和退出时具体做什么的函数名字,比如hello_init和hello_exit(随便起)。
3.模块参数的函数
(1)宏描述
使用宏MODULE_PARM_DESC(variable_name, "xxx")对定义的参数进行说明
参数:
一般是static静态的
(2)一般变量
module_param(int_name, type, perm); module_param_named(ext_name, int_name, type, perm);
参数:
ext_name:(external)用户看到的参数名,对外的参数名int_name:(internal)当参数中没有ext_name的时候,此参数即是用户看到的参数名,又是模块内接受参数的变量。type:表示参数的数据类型的标识符byte, short, ushort, int, uint, long, ulong, charp(重点), bool, invbool charp是指向字符串的指针,而不是char类型,也不能填char。perm:指定了在sysfs中相应文件的访问权限。(一般不考虑,0或S_IRUSR即可)
(3)字符串
一种是charp的字符串指针形式,内外名一致另一种是string的字符串数字形式,内外名可异名
module_param_string(ext_name, string, len, perm);
参数:
ext_name:展现给用户插入是的参数名,MODULE_PARM_DESC()使用的是这个而不是stringstring:存储内容的字符串数组变量名len:字符串的长度,不像charp是动态的,这个是预先分配的,一般就填字符串数组的长度。perm:0
(4)数组
module_param_array(int_name, type, nump, perm); module_param_array_named(ext_name, int_name, type, nump, perm);
参数:
nump:系统动态判断出有多少个参数存放在数组中,然后将其写入一个整数变量的地址中,如&narr(类似scanf("%d",&narr))。 动态写入,不加static
4.学术规范
MODULE_DESCRIPTION("a simple driver module");
MODULE_LICENSE("GPL");
三、实验
1.任务
编写一个内核模块; 编译该模块; 加载、卸载该模块;
2.过程
(1)编写内核模块.c文件
mkdir project
&& cd project
gedit catkinModule.c
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
static int year
= -1;
module_param(year
, int, 0);
MODULE_PARM_DESC(year
, "内外同名的int型的整数");
static int month
= -1;
module_param_named(month_ext
, month
, int, 0);
MODULE_PARM_DESC(month_ext
, "内外异名的int型的整数");
static char *direction
= "default";
module_param(direction
, charp
, 0);
MODULE_PARM_DESC(direction
, "指针形式的字符串");
#define LEN 10
static char subject
[LEN
];
module_param_string(subject_ext
, subject
, LEN
, 0);
MODULE_PARM_DESC(subject_ext
, "数组形式的字符串");
static int date_array
[3];
int narr
;
module_param_array(date_array
, int, &narr
, 0);
MODULE_PARM_DESC(date_array
, "int数组");
static int date_array2
[3];
int narr2
;
module_param_array_named(date_array2_ext
, date_array2
, int, &narr2
, 0);
MODULE_PARM_DESC(date_array2_ext
, "int数组");
static int __init
hello_init(void)
{
printk(KERN_ALERT
"Hello, my LKM.\n");
printk(KERN_ALERT
"[int]year:%d.\n", year
);
printk(KERN_ALERT
"[int]month:%d.\n", month
);
printk(KERN_ALERT
"[charp]direction:%s.\n", direction
);
printk(KERN_ALERT
"[string]subject:%s.\n", subject
);
printk(KERN_ALERT
"[array]date_array:\n");
int i
;
for (i
= 0; i
< narr
; i
++)
{
printk(KERN_ALERT
"(%d) %d\n", i
, date_array
[i
]);
}
printk(KERN_ALERT
"[array]date_array2:\n");
int j
;
for (j
= 0; j
< narr2
; j
++)
{
printk(KERN_ALERT
"(%d) %d\n", j
, date_array2
[j
]);
}
return 0;
}
static void __exit
hello_exit(void)
{
printk(KERN_ALERT
"Bye, my LKM.\n");
}
module_init(hello_init
);
module_exit(hello_exit
);
MODULE_DESCRIPTION("a simple driver module");
MODULE_LICENSE("GPL");
(2)编写Makefile文件
gedit Makefile
obj-m :
= catkinModule.o
KERNELDIR :
= /lib/modules/
$(shell uname -r)/build
PWD :
= $(shell pwd)
modules:
$(MAKE) -C
$(KERNELDIR) M
=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules.order Module.symvers
(3)编译
make
PS: 如果重新编译的话,要把之前残留的垃圾删除(试过不管这些垃圾也能编译成功,但编辑结果还是原来的就很气)
make clean
make
(4)安装模块:
先清理一下缓存,不然一会就可能输出一大堆多余东西,影响到我们想要看到的输出东西
sudo dmesg -c
安装
sudo insmod catkinModule.ko year
=2020 direction
=embeddedSystem subject_ext
=software date_array
=2020,3,10 date_array2_ext
=2020,3,11
【可选部分】:
查看系统已经加载的模块:
lsmod
| grep catkinModule
查看系统已经加载的模块信息:
modinfo catkinModule.ko
这是.ko文件,所以要在存在这个文件的目录(在这里就是当前编译的目录)下才能查看到。
(5)查看printk的输出在缓冲区的信息:
sudo dmesg
(6)卸载模块:
sudo rmmod catkinModule
(7)清除printk输出在缓存区的信息:
sudo dmesg -c
四、问题
1.for循环初始化声明分开
for (int i
= 0; i
< narr
; i
++)
会报错:
解决:要么使用c99,要么就注意点分开写
int i
;
for (i
= 0; i
< narr
; i
++)
2.先卸载残留才能安装
先卸载:
sudo rmmod module_name
3.震惊!printk居然不能输出%c
出来是\x17之类的十六进制结果