Linux内核模块开发
内核模块
一种机制能让内核文件(zImage或者bzImage)本身并不包含某组件,而是在该组件需要被使用的时候,再动态的增加到内核。
内核模块特点:
- 内核本身并不被编译到内核文件;
- 可以根据需求,在内核运行期间动态的安装或卸载;
内核模块程序范例
#include<linux/init.h>
#include<linux/module.h>
static int hello_int(void)
{
prinfk(KERN_WARNING"Hello, world!\n");
return 0;
}
static void hello_exit(void)
{
prinftk(KERN_INFO"Goodbye, world\n");
}
module_init(hello_init);
module_exit(hello_exit);- 模块加载函数(必需)
安装模块时被系统自动调用的函数,通过module_init宏来指定。 - 模块卸载函数(必需)
卸载模块时被系统自动调用的函数,通过module_exit宏来指定。
内核模块编译
使用makefile.
- 内核模块由一个源文件组成,该如何编写makefile?
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KDIR := /lib/modules/2.6.18-53.e15/build
all:
make -C $(KDIR) M=$(PWD) modules # -C进入该目录下,执行该目录下的makefile文件 M表示当前代码所在路径 modules表示模块
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif- 内核模块由多个源文件组成,该如何编写makefile?
ifneq ($(KERNELRELEASE),)
obj-m := mymodule.o # 最终生成的执行文件名称
mymodule-objs := main.o add.o # 所有有关的源文件
else
KDIR := /lib/modules/2.6.18-53.e15/build
all:
make -C $(KDIR) M=$(PWD) modules # -C进入该目录下,执行该目录下的makefile文件 M表示当前代码所在路径 modules表示模块
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif内核模块安装与卸载
- 加载 insmod
- 卸载 rmmod
- 查看 lsmod
- 加载 modprobe: 如果insmod,也是加载一个模块到内核。但是它更加强大,会自动寻找相关内容,自动加载。
内核模块可选参数
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("GPL") //遵循的开源协议
MODULE_AUTHOR("Your name")//作者申明
MODULE_DESCRIPTION("Hello World Module")//模块描述
MODULE_ALIAS("a simplest module")//模块别名
static int hello_int(void)
{
prinfk(KERN_WARNING"Hello, world!\n");
return 0;
}
static void hello_exit(void)
{
prinftk(KERN_INFO"Goodbye, world\n");
}
module_init(hello_init);
module_exit(hello_exit);
module_param(name,type,perm):模块参数->指定模块参数,模块参数用于加载模块时传递参数给模块。
- name 是模块参数的名称
- type 是这个参数的类型 ->常见类型:bool 布尔型, int 整型, charp 字符串型
- perm 是模块参数的访问类型 -> S_IRUGO:任何用户都对/sys/module中出现的该参数具有读权限;S_IWUSR:允许root用户修改/sys/module中出现的该参数;
例如:int a = 3; module_param(a,int,S_IRUGO);
或者char *st; module_param(st,charp,S_IRUGO);
具体例子:
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("GPL") //遵循的开源协议
MODULE_AUTHOR("Your name")//作者申明
MODULE_DESCRIPTION("Hello World Module")//模块描述
MODULE_ALIAS("a simplest module")//模块别名
static char *name = "dengbo sun";
static int age = 30;
module_param(age,int,S_IRUGO);
module_param(name,charp,S_IRUGO);
static int hello_int(void)
{
prinfk(KERN_WARNING"Hello, world!\n");
prinfk(KERN_EMERG"Name:%s\n",name);
prinfk(KERN_EMERG"Age:%d\n",age);
return 0;
}
static void hello_exit(void)
{
prinftk(KERN_INFO"Goodbye, world\n");
}
module_init(hello_init);
module_exit(hello_exit);$ insmod hello.ko age = 32
内核符号导出
/proc/kallsyms中记录了所有导出的符号名称和地址
为什么需要内核符号导出?方便其他模块使用该模块的函数
内核符号导出方式
EXPORT_SYMBO(func name);
常见问题
- 版本不匹配:
使用uname -r查看当前内核版本。
解决办法:- 使用
modprobe --force-modversion强行插入内核. - 找到相符合的内核版本.
- 使用
总结
与应用程序区别在于,应用程序从main函数执行完成就没了。内核模块则一直都在,除非卸载。
内核打印
printfk // 只能在内核内使用,并且有优先级
/*优先级*/
KERN_EMERG ->"0"
KERN_ALERT ->"1"
KERN_CRIT ->"2"
KERN_ERR ->"3"
KERN_WARNING ->"4"
KERN_NOTICE ->"5"
KERN_INFO ->"6"
KERN_DEBUG ->"7"
printf // 只能在应用程序内使用

京公网安备 11010502036488号