环境:主机-Ubuntu 16.04,开发板-友善之臂tiny4412开发板,内核版本linux-3.5
参考《Linux设备驱动开发详解 基于最新的Linux 4.0内核》(宋宝华 编著)
一个Linux内核模块主要由以下几部分组成:
1、模块加载函数
当通过insmod或modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。
2、模块卸载函数
当通过rmmod命令卸载模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。
3、模块许可证
许可证(LINCESE)声明描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到内核被污染(Kernel Tainted)的警告。在Linux内核模块领域,可接受的LICENSE包括”GPL“、"GPL v2"、"GPL and additional rights" ...等。
4、模块参数(可选)
模块参数是模块被加载的时候可以传递给它的值,它本身对应模块内部的全局变量。
5、模块导出符号(可选)
内核模块可以导出的符号(symbol,对应于函数或变量),若导出,其他模块则可以使用本模块的变量或函数。
6、模块作者等信息声明(可选)
代码:
#include
#include
int __init hello_init(void)
{
printk("Hello World enter ++\n");
return 0;
}
void __exit hello_exit(void)
{
printk("Hello world exit --\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("https://blog.csdn.net/qq_30155503");
MODULE_LICENSE("GPL v2");
1、加载:Linux内核模块加载函数一般以"__init"标识声明,如“int __init hello_init(void)”函数;通过module_init()函数指定加载函数,如“module_init(hello_init);”,指定加载函数为hello_init();当通过insmod或modprobe命令加载内核模块时,hello_init()将被自动执行;
2、卸载:Linux内核模块加载函数一般以"__exit"标识声明,如“void __exit hello_exit(void)”函数;通过module_exit()函数指定卸载函数,如“module_exit(hello_exit);”,指定卸载函数为hello_exit();当通过rmmod命令卸载模块时,hello_exit()将被自动执行。
3、MODULE_AUTHOR();是作者信息,MODULE_LICENSE();是模块许可证声明。
通常,加载函数完成初始化工作、申请资源等,卸载函数则完成释放资源、去初始化工作;两者的功能是相反的。
Makefile代码:
# to build modules
obj-m := hello.o
KERNELDIR ?= /data/arm-linux/kernel/tiny4412/linux-3.5
PWD := $(shell pwd)
all: modules
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *.ko *mod* *.sy* *ord* .*cmd .tmp*
注释:
obj-m:表示编译成模块,obj-y表示编译进内核。
KERNELDIR:内核源码的路径,编译与运行环境的内核版本应一致,即与ARM板上跑的linux系统的版本一致,“?=”表示若为空则为之赋值。(此处我指定的路径为tiny4412开发板附带的3.5版本内核)
当执行make clean时,当清除make时所生成的所有文件。
编译:make
错误信息:
ERROR: Kernel configuration is invalid.
include/generated/autoconf.h or include/config/auto.conf are missing.
Run 'make oldconfig && make prepare' on kernel src to fix it.
原因:内核没有编译好,要先编译成功内核才要用,提示你运行“make oldconfig && make prepare”,最终可行的执行如下:
make oldconfig && make prepare && make scripts
编译通过:
make -C /data/arm-linux/kernel/tiny4412/linux-3.5 M=/data/project/driver/hello modules
make[1]: Entering directory '/data/arm-linux/kernel/tiny4412/linux-3.5'
CC [M] /data/project/driver/hello/hello.o
Building modules, stage 2.
MODPOST 1 modules
CC /data/project/driver/hello/hello.mod.o
LD [M] /data/project/driver/hello/hello.ko
make[1]: Leaving directory '/data/arm-linux/kernel/tiny4412/linux-3.5'
在当前目录生成了内核模块文件 hello.ko
首先,将生成的内核模块文件hello.ko移到开发板上
1、加载模块:执行insmod命令进行加载
[root@FriendlyARM /mnt]# ls
hello.ko
[root@FriendlyARM /mnt]# insmod hello.ko
[ 909.890000] Hello World enter ++
2、查看模块:执行命令lsmod可查看当前加载的内核模块
[root@FriendlyARM /mnt]# lsmod
hello 570 0 - Live 0xbf278000 (O)
libertas_sdio 8423 0 [permanent], Live 0xbf25a000 (O)
libertas 54371 1 libertas_sdio,[permanent], Live 0xbf247000 (O)
zd1211rw 44215 0 [permanent], Live 0xbf237000 (O)
rt2800usb 12324 0 [permanent], Live 0xbf22f000 (O)
可见,确实有hello模块存在。
3、卸载模块:执行rmmod命令进行卸载
[root@FriendlyARM /mnt]# rmmod hello
[ 976.995000] Hello world exit --
注意:加载时的参数是文件名("xxx.ko"),卸载时是模块名("hello")。
由上可见,加载、卸载模块时确实是自动执行了指定的加载函数与卸载函数。