这里给出一个Linux内核模块的最简单的示例程序,当然,真实的驱动程序要比这个复杂。
#include <linux/init.h>
#include <module.h>
static int xxx_init(void)
{
printk(KERN_INFO "Hello Linux kernel!");
return 0;
}
static void xxx_exit(void)
{
printk(KERN_INFO, "Goodbye Linux kernel!");
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_AUTHOR("Jack");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("An example linux driver program");
通过以上这个程序,大家可以对Linux驱动程序有个初步的感性认识。
这里有几点说明:
1. Linux整体上可以划分为用户空间和内核空间,我们之前见过的那些“普通的”程序就是位于用户空间的,例如之前给出的test.c程序,而这里给出的驱动程序是位于内核空间的。这一点要注意区分。
2. 从上面的程序中我们看到,打印信息时我们使用的是printk函数,而不是之前一直用的printf。printk就是内核空间的打印函数。在驱动程序中添加打印信息都要使用这个函数。
3. printk函数与printf不同的一点是里面有个KERN_INFO,这个代表调试打印级别。还可以用别的级别,例如KERN_DEBUG,KERN_ERR等。可以设置不同的调试打印级别,这个对于调试来说是很方便的。在程序中可能会有很多的打印信息,有时候我们调试问题只需要知道某些信息,根据信息的重要程度,可以把他们设置为不同的打印级别,如果我们是在调试程序,需要很详细的打印信息,就可以把DEBUG级别的信息全部打开。如果我们产品已经调试完毕准备发布正式版本了,则可以把打印级别调高,只打印那些最重要的信息,例如出错信息,而详细的调试信息就不必打印了。在控制台使用命令可以更改调试信息的打印级别。
4. module_init(xxx_init);这句话告诉内核xxx_init函数是这个驱动的初始化函数,即该驱动被加载时首先进入这个函数。函数中会执行一些初始化工作,例如分配内存和其他一些资源。
5. module_exit(xxx_exit);告诉内核xxx_exit函数是这个驱动的卸载函数,即该驱动被卸载时会执行这个函数。函数中会释放在xxx_init函数中分配的内存和资源。
6. MODULE_LICENSE("Dual BSD/GPL");声明了该驱动程序遵循的开源许可证类型。
7. 驱动程序经过交叉编译,生成一个.ko类型的文件。通过
insmod xxx.ko
命令加载到内核中。
如果想卸载该驱动,则使用
rmmod xxx.ko。