首先贴代码helloworld.c和Makefilelinux
/************************************************************************* > File Name: helloworld.c > Author: hailin.ma > Mail: mhl2018@126.com > Created Time: Wed 15 Jul 2015 02:39:35 PM CST ************************************************************************/ #include <linux/init.h> #include <linux/module.h> static char* mod_name = "hello"; static int mod_num = 1; static int __init hello_init(void) { printk(KERN_INFO"hello world init! mod_name = %s,mod_num = %d\n",mod_name,mod_num); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO"hello world exit!\n"); } module_init(hello_init); module_exit(hello_exit); module_param(mod_name,charp,S_IRUGO); module_param(mod_num,int,S_IRUGO); MODULE_AUTHOR("hailin.ma <mhl2018@126.com>"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("A simple hello world module"); MODULE_ALIAS("simple driver test");
Makefileshell
#CFLAGS = -g KVERS = /lib/modules/$(shell uname -r)/build obj-m += hello.o hello-objs:=helloworld.o all: make -C $(KVERS) M=$(PWD) modules @rm *.o clean: make -C $(KVERS) M=$(PWD) clean
make 执行后将生成hello.ko文件,命令:insmod hello.ko 便可加载模块到内核。加载 hello.ko 后,内核中将包含
/sys/module/hello 目录,该目录下又包含一个 refcnt 文件和一个 sections 目录,在/sys/module/hello
目录下运行“ tree –a” 获得以下目录树:数组
modinfo <模块名 >命令能够得到模块的信息,包括模块做者、模块的说明、模块所支持
的参数以及 vermagic:函数
insmod 加载模块ui
lsmod 查看已加载模块,
lsmod 命令其实是读取并分析“/proc/modules” 文件。
编码
rmmod 卸载模块spa
tail /var/log/messages 查看printk打印消息命令行
一个 Linux 内核模块主要由以下几个部分组成。
( 1 )模块加载函数(通常须要)。
当经过 insmod 或 modprobe 命令加载内核模块时,模块的加载函数会自动被内核执行,完成
本模块的相关初始化工做。3d
static int _ _init initialization_function(void) //初始化函数 { /* 初始化代码 */ } module_init(initialization_function); //指定模块初始化函数
模块加载函数必须以“ module_init(函数名 )” 的形式被指定。它返回整型值,若初始化成功,
应返回 0。而在初始化失败时,应该返回错误编码。在 Linux 内核里,错误编码是一个负值,在
<linux/errno.h>中定义,包含-ENODEV、 -ENOMEM 之类的符号值。老是返回相应的错误编码是
种很是好的习惯,由于只有这样,用户程序才能够利用 perror 等方法把它们转换成有意义的错误
信息字符串。
在 Linux 2.6 内核中,可使用 request_module(const char *fmt, …)函数加载内核模块,驱动开
发人员能够经过调用
request_module(module_name);
或
request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev));
这种灵活的方式加载其余内核模块。
在 Linux 中,全部标识为_ _init 的函数在链接的时候都放在.init.text 这个区段内,此外,全部
的_ _init 函数在区段.initcall.init 中还保存了一份函数指针,在初始化时内核会经过这些函数指针
调用这些_ _init 函数,并在初始化完成后,释放 init 区段(包括.init.text、 。initcall.init等)。指针
( 2)模块卸载函数(通常须要)。
当经过 rmmod 命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块卸载函数
相反的功能。
static void _ _exit cleanup_function(void) //模块卸载函数 { /* 释放代码 */ } module_exit(cleanup_function); //指定模块卸载函数
模块卸载函数在模块卸载的时候执行,不返回 任何值,必须以“ module_exit(函数名 )” 的形
式来指定。
一般来讲,模块卸载函数要完成与模块加载函数相反的功能, 以下所示。
! 若模块加载函数注册了 XXX,则模块卸载函数应该注销 XXX。
! 若模块加载函数动态申请了内存,则模块卸载函数应释放该内存。
! 若模块加载函数申请了硬件资源(中断、 DMA 通道、 I/O 端口和 I/O 内存等)的占用,
则模块卸载函数应释放这些硬件资源。
! 若模块加载函数开启了硬件,则卸载函数中通常要关闭之。
和_ _init 同样, _ _exit 也可使对应函数在运行完成后自动回收内存。实际上, _ _init 和_ _exit
都是宏,其定义分别为:
#define _ _init _ _attribute_ _ (( _ _section_ _ (".init.text")))
和
#ifdef MODULE
#define _ _exit _ _attribute_ _ (( _ _section_ _(".exit.text")))
#else
#define _ _exit _ _attribute_used_ _attribute_ _ (( _ _section_ _(".exit.text")))
#endif
数据也能够被定义为_ _initdata 和_ _exitdata,这两个宏分别为:
#define _ _initdata _ _attribute_ _ (( _ _section_ _ (".init.data")))
和
#define _ _exitdata _ _attribute_ _ (( _ _section_ _(".exit.data")))
( 3)模块许可证声明(必须)。
许可证( LICENSE)声明描述内核模块的许可权限,若是不声明 LICENSE,模块被加载时,
将收到内核被污染 ( kernel tainted)的警告。
在 Linux 2.6 内核中,可接受的 LICENSE 包括“GPL”、“GPL v2”、“GPL and additional rights”、
“ Dual BSD/GPL”、 “ Dual MPL/GPL” 和“ Proprietary”。
大多数状况下,内核模块应遵循 GPL 兼允许可权。 Linux 2.6 内核模块最多见的是以
MODULE_LICENSE( "Dual BSD/GPL" )语句声明模块采用 BSD/GPL 双 LICENSE。
( 4)模块参数(可选)。
模块参数是模块被加载的时候能够被传递给它的值,它自己对应模块内部的全局变量。
咱们能够用 “ module_param(参数名 ,参数类型,参数读/写权限)” 为模块定义一个参数,例以下
列代码定义了 1 个整型参数和 1 个字符指针参数:
static char *book_name = " dissecting Linux Device Driver "; static int num = 4 000; module_param(num, int, S_IRUGO); module_param(book_name, charp, S_IRUGO);
在装载内核模块时,用户能够向模块传递参数,形式为“ insmode(或 modprobe)模块名 参
数名 =参数值”,若是不传递,参数将使用模块内定义的缺省值。
参数类型能够是 byte、 short、 ushort、 int、 uint、 long、 ulong、 charp(字符指针)、 bool 或 invbool
(布尔的反),在模块被编译时会将 module_param 中声明的类型与变量定义的类型进行比较,判断
是否一致。
模块被加载后,在/sys/module/目录下将出现以此模块名命名的目录。当“参数读/写权限” 为 0
时,表示此参数不存在 sysfs 文件系统下对应的文件节点,若是此模块存在“参数读/写权限” 不为 0
的命令行参数,在此模块的目录下还将出现 parameters 目录,包含一系列以参数名命名的文件节点,
这些文件的权限值就是传入 module_param()的“参数读/写权限”,而文件的内容为参数的值。
除此以外,模块也能够拥有参数数组,形式为“module_param_array(数组名,数组类型,数组长,参数
读/写权限)”。从 2.6.0~2.6.10 版本, 需将数组长变量名赋给“数组长”,从 2.6.10 版本开始, 需将数组
长变量的指针赋给“数组长”,当不须要保存实际输入的数组元素个数时,能够设置“数组长” 为 NULL。
运行 insmod,不一样参数用空格隔开
( 5)模块导出符号(可选)。
内核模块能够导出符号( symbol,对应于函数或变量),这样其余模块可使用本模块中的变
量或函数。