最近开始学习Linux驱动开发,以LDD3为主,同时参考宋宝华的《Linux设备驱动开发详解》以及网上的一些资料,记录下自己的学习过程,备忘。
一、内核版的Hello world 模块:
#include #include MOUDLE_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);
|
关于这个程序的说明:
1,大部分头文件都包含下面两行代码:
module.h
包含有可装载模块需要的大量符合和函数的定义。包含init.h 的目的是指定初始化和清除函数。另外,大部分模块还包括moduleparam.h,这样我们可以在装载模块的时想模块传递函数。
2,模块应指定代码所使用的许可证,即:MOUDLE_LICENSE("Dual BSD/GPL");
如果不声明LICENSE,模块被加载是,将受到内核被污染(kernel tainted)的警告。
此外还有可选的其他描述性定义
MODULE_AUTHOR("");
MODULE_DESCRIPTION("");
MODULE_VERSION("");
MODULE_ALIAS("");
MODULE_DEVICE_TABLE("");
|
上述MODULE_声明习惯上放在文件最后。
3,static int hello_init(void) 模块初始化函数。典型的模块加载函数形式如下:
static int _ _initinitialization_function(void)
{
/*初始化代码*/
}
module_init(initialization_function);
|
初始化函数应该被声明为static,因为这种函数在特定文件之外没其他意义。
__init对内核来讲是种提示,表明该函数仅在初始化期间使用。在模块被装载之后,模块装载器会将该函数扔掉,将初始化花占用的内存释放出来。
module__init 是强制使用的,没有这个定义,初始化这个函数永远不会被调用。这个宏会在模块化的目标代码中增加一个特殊的段,用于说明内核初始化函数所在的位置。
4,printk函数在Linux内核中定义,和printf类似,最大区别在于缺乏对浮点数的支持。
KERN_ALERT定义了这条消息的优先级,原因是具有默认优先级的消息可能不会输出到控制台上。也就是说,没有KERN_ALERT,有可能正常执行了但是看不到打印的消息。
5,static void hello_exit(void)模块清除函数。典型的模块清除函数形式如下:
static int _ _exit cleanup_function(void) { /*清除代码*/ } module_exit(cleanup_function);
|
如果模块被直接内嵌到内核中,或者内核中的配置不允许卸载模块,那么__exit的函数将被简单的丢弃。因此该函数只能在被卸载后再系统关闭的时候被调用。
另外,如果一个函数未定义清除函数,则内核不允许卸载该模块。
二、编译和装载
1,将该程序保存为hello.c,编写一个最简单的Makefile,在Makefile中输入如下一行:
2, 执行命令 make -C /usr/src/linux-headers-2.6.32-24-generic/ M=$(pwd) modules,之后生成hello.ko模块。
-C选项指定的是Linux内核源码所在的目录,M=后指定的是hello.c和Makefile所在的目录。
3,Makefile语法:obj-m := hello.o 代表了我们要构造的模块名为 hell.ko,make 会在该目录下自动找到hell.c文件进行编译。如果hello.o是由其他的源文件生成(比如file1.c和 file2.c)的,则在下面加上(注意红色字体的对应关系):
hello-objs := file1.o file2.o ......
更加详细的内核Makefile语法可参考以下两篇文档:
4,通过insmod ./hello.ko 命令可以加载该模块,输出“hello world!”,
通过rmmod ./hello 命令可以卸载hello模块,输出“Goodbye,cruel world!”。
三、模块参数
为了增加驱动程序的灵活性,内核允许对驱动程序指定参数,而这些参数可在加载驱动程序模块时改变。
这些参数的值可由insmod或者modprobe在加载时指定;后者也可以从它的配置文件(/etc/modprobe.conf)读取参数的值。这两个命令可在命令行里接受几种参数类型的赋值。
我们可以用“module_param(参数名,参数类型,参数读/写权限)”为模块定义参数。可以对hello模块)做一些改进。 我们增加 2个参数:一个整型值,称为howmany,一个字符串称为whom。 在装载这个增强的模块时,将向whom问候howmany次。这样我们可以用下面的命令来装载该模块:
insmod hellop.ko howmany=5 whom=“Students”
|
hellop.c代码如下:
#include #include MODULE_LICENSE("Dual BSD/GPL");
static char *whom = "world"; static int howmany = 1;
module_param(howmany, int, S_IRUGO); module_param(whom, charp, S_IRUGO);
static int hello_init(void) { int i; for(i=0;i { printk(KERN_ALERT "Hello %s\n",whom); } return 0; } static void hello_exit(void) { printk(KERN_ALERT "Hello world exit\n"); } module_init(hello_init); module_exit(hello_exit);
|
内核支持的模块参数类型包括byte、short、ushort、int、uint、long、ulong、charp(字符指针)、bool 或invbool(布尔的反),以‘u’开头的为无符号值。
除此之外,模块也可以拥有参数数组,形式为“module_param_array(数组名,数组类型,数组长,参数读/写权限)”。运行 insmod或modprobe命令时,应使用逗号分隔输入的数组元素。
阅读(1381) | 评论(0) | 转发(0) |