新手学Linux 不妥的地方请指正,不完善的地方请补充。
源码:
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
/***************************************************************************/
该模块定义了两个函数,其中hello_init()在模块加载时被调用,hello_exit()在模块被去除时调用。module_init和module_exit是用特别的宏来指出这两个函数的角色。
下面主要是对驱动代码进行解读:
1. 头文件
<linux/init.h>:这个头文件包含了模块初始化和清除函数;
<linux/module.h>:这个头文件包含了与模块加载有关的符号和函数
2. MODULE_LICENSE
从2.4.10版本的内核开始,模块必须通过MODULE_LICENSE宏声明此模块的许可证,否则在加载此模块时,会收到内核"kernel tainted"警告。
MODULE_LICENSE("GPL"):GPL声明这是GNU General Public License的任意版本
MODULE_LICENSE("GPL v2")声明为GPL的第二版
MODULE_LICENSE("GPL and additional"):
MODULE_LICENSE("Dual BSD/GPL"):声明模块采用BSD/GPL双license
MODULE_LICENSE("Dual MPL/GPL"):声明模块采用MPL/GPL双license
MODULE_LICENSE("Proprietary"):除非声明为一个开源版本,否则内核默认为私有模块。
MODULE_AUTHOR:声明作者
MODULE_DESCRIPTION:对模块的简单描述
MODULE_VERSION:声明模块版本
MODULE_ALIAS:声明模块的别名
MODULE_DEVICE_TABLE:告诉用户空间这个模块支持什么样的设备。
3. printk()函数
Printk函数式直接使用了向终端写函数tty_write(),区别于printf()函数,它是调用write()系统调用函数向标准输出设备输出打印信息。Printk()带有优先级,一共定义了八个优先级的宏,这个可以从linux/kernel.h中查看,如下:
printk()是内核输出,在终端是看不见的,可以通过dmesg命令查看系统日志,获取内核输出信息。
/***************************************************************************/
了解了以上内容,接下来就可以测试写的模块了。
编译模块,首先是要写Makefile文件,参考如下:
ifneq ($ (KERNELRELEASE),) // 是比较KERNELRELEASE和NULL,即 KERNELRELEASE不为空,则执行下面的语句
obj-m := hello.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
/***************************************************************************/
装载目标模板:insmod ./hello.ko
可以通过lsmod命令查看目前安装的驱动模块。
卸载目标模板:rmmod ./hello.ko
对于printk()的打印信息,可以是用命令dmesg或者在目录/var/log/syslog或者目录/var/log/kern.log下查看。
/***************************************************************************/
以上便是对驱动的初步认识。