今天从网上看了个视频教程,关于内核模块开发的。网络上有许多这样的文章,应该比我这个详细。
写这篇博客的目的是做一下笔记,便于自己查阅。
首先给出内核模块源代码,当然是最最简单的helloworld。
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
//GPL 开源协议
static int hello_init(void)
{
printk(KERN_ALERT "hello module!\n"); //内核打印信息函数,相当于应用层的printf();
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "bye module!\n");
}
module_init(hello_init); //必须使用的,模块加载函数。向内核注册
module_exit(hello_exit); //必须使用的,模块卸载函数注册
需要的Makefile
ifneq ($(KERNELRELEASE),) //判断KERNELRELEASE不能与NULLobj-m:=hello.o //目标模块的名称
else
#generate the path
CURRENT_PATH:=$(shell pwd) //获得当前目录的名称并赋值给CURRENT_PATH变量
#the absolute path // 是用绝对路径
LINUX_KERNEL_PATH:=/lib/modules/$(shell uname -r)/build //内核模块所依赖的内核编译路径,shell uname -r获得当前内核版本号
#complie object
default:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules //make -C 表示到$(LINUX_KERNEL_PATH)使用它的makefile编译。
//M=$(CURRENT_PATH)表示你的内核源代码的位置
// modules :编译成为模块
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
endif
编译一次内核模块需要两次执行上述makefile,第一次执行的时候KERNELRELEASE是没有值得,第二次执行时KERNELRELEASE是有值得。需要
注意此处
如果多个C文件进行的编译一个内核模块。Makefile则需要写为如下格式
ifneq ($(KERNELRELEASE),) //判断KERNELRELEASE不能与NULL
obj-m:=hello.o //目标模块的名称
hello.o-objs :=file1.o file2.o file3.o ... //(可以最近多个) ------>如果一个源文件可以用这个方式生产与文件名不同的内核模
else
#generate the path
CURRENT_PATH:=$(shell pwd) //获得当前目录的名称并赋值给CURRENT_PATH变量
#the absolute path // 是用绝对路径
LINUX_KERNEL_PATH:=/lib/modules/$(shell uname -r)/build //内核模块所依赖的内核编译路径,shell uname -r获得当前内核版本号
#complie object
default:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules //make -C 表示到$(LINUX_KERNEL_PATH)使用它的makefile编译。
//M=$(CURRENT_PATH)表示你的内核源代码的位置
// modules :编译成为模块
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
endif
编译得到hello.ko,然后insmod hello.ko加载模块,rmmod hello卸载模块。
查看内核模块lsmod。
modprobe hello:也是加载一个模块,但是不同于insmod的是,它会在/lib/modules/$<$version>/modules.dep查看要加载的模块,看它
是否依赖于其他的模块,如果有modprobe会先找到那些被依赖模块并先安装它们。
--------------------------------华丽的分割线---------------------------------------
内核参数:
1.定义模块参数的方法:
module_param(name, type, perm);
其中,name:表示参数的名字;
type:表示参数的类型;
perm:表示参数的访问权限;
static int num=10;
module_param(num,int,S_IRUGO);
static int hello_init(void)
{
printk("Hello module init./n");
printk("num=%d/n",num);
return 0;
}
static void hello_exit(void)
{
printk("Goodbye module exit./n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("a simple module");
MODULE_ALIAS("hello");
shell $ insmod hello.ko num=100 即可改变num的值。
-------------------------------------------华丽分割线-----------------------------------
内核模块符号导出
EXPORT_SYMBOL(函数名/变量名)
即当一个内核模块调用另外一个内核模块的函数或者变量时,需要再被调用的内核模块中将变量或者函数使用EXPROT_SYMBOL 将对应函数或者变量导出,才能
正常使用。关于这部分内容网络上有许多博文,具体使用时请参考其他博文吧。
----------------------------华丽分割线----------------------------------------------
内核打印&级别。
/proc/sys/kernel/printk中定义着其他控制台和其他log文件的打印级别。
其他的就不再赘述了,网上有很多博文了。
-----------------------------------华丽结束线-----------------------------------