#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
}
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这几行使用了特别的内核宏来指出这两个函数的角色。另一个特别的宏(MODULE_LICENSE)是用来告知内核,该模块带有一个自由的许可证,没有这样的说明,在模块加载时内核会报错。
printk函数在Linux内核中定义并且对模块可用,它与标准C库函数printf的行为相似。内核需要它自己的打印函数,因为没有C库的支持。字串KERN_ALERT是消息的优先级。在此模块中指定了一个高优先级,因为使用默认优先级的消息可能不会直接显示,这依赖于运行的内核版本、klogd守护进程的版本以及配置。
为了编译模块文件,有两种方法创建Makefile文件可以实现,
1、只需一行即可,命令如下:
obj-m := hello.o
obj-m指出将要编译成的内核模块列表。*.o 格式文件会自动地由相应的 *.c 文件生成(不需要显式地罗列所有源代码文件)
如果要把上述程序编译为一个运行时加载和删除的模块,则编译命令如下所示。
make -C /usr/src/kernels/2.6.25-14.fc9.i686 M=$PWD modules
这个命令首先是改变目录到用 -C 选项指定的位置(即内核源代码目录,这个参数要根据自己的情况而定)。这个 M= 选项使Makefile在构造modules目标前,返回到模块源码目录。然后,modules目标指向obj-m变量中设定的模块。这里的编译规则的意思是:在包含内核源代码位置的地方进行make,然后再编译 $PWD (当前)目录下的modules。这里允许我们使用所有定义在内核源代码树下的所有规则来编译我们的内核模块。
2、使用下面的Makefile来实现:
ifneq ($(KERNELRELEASE),) obj-m := helloworld.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD :=$(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif
root@li-desktop:/opt# make make -C /lib/modules/2.6.38-11-generic/build M=/opt modules make[1]: 正在进入目录 `/usr/src/linux-headers-2.6.38-11-generic' CC [M] /opt/hello.o Building modules, stage 2. MODPOST 1 modules CC /opt/hello.mod.o LD [M] /opt/hello.ko make[1]:正在离开目录 `/usr/src/linux-headers-2.6.38-11-generic'
编译完毕之后,就会在源代码目录下生成hello.ko文件,这就是内核驱动模块了。我们使用下面的命令来加载hello模块。
insmod hello.ko
下列命令完成相反的过程,即卸载hello模块。
rmmod hello
这时,你会发现终端里什么输出也没有,不用急,因为printk是内核输出函数,要查看的话,还要执行下列指令。
dmesg | tail
这时,在终端里就会打印出内核信息了,如下图所示。
。同时,也可以使用lsmod命令来查看是否有加载了
root@lip-desktop:/opt# lsmod
Module Size Used by
helloworld 12448 0
nls_iso8859_1 12617 1
至此,一个最简单的内核模块驱动程序就完成了。^_^