linux设备驱动开发基础(二)

参考:《Linux内核驱动开发入门与实战》

1 驱动模块的组成:

        一个驱动模块主要由如下部分组成,如图下图所示。这是一个规范的驱动模块应该包含的结构。这些结构在图中的顺序也是在源文件中的顺序。

头文件(可选)

模块参数(可选)

模块功能函数(可选)

其他(可选)

模块加载函数(必须)

模块卸载函数(必须)

模块许可声明(必须)

1.1 头文件:

         驱动模块会使用内核中的许多函数,所以要包含必要的头文件。有两个头文件是所有驱动模块都必须包含的。这两个头文件是:

#include <linux/module.h>

#include <linux/init.h>

module.h文件包含了加载模块时需要使用的大量符号和函数定义。

Init.h文件包含了模块加载函数和模块释放函数的宏定义。

1.2 模块参数

        模块参数是驱动模块加载时,需要传递给驱动模块的参数。如果一个驱动模块需要完成两种功能,那么就可以通过模块参数选择使用哪一种功能。

        模块参数定义:

        static long a = 1;

        module_param(a, long, S_IRUGO);

        使用:insmod xxx.ko  a = 1;

1.3 模块加载函数

        模块加载函数是模块加载时,需要执行的函数。

        module_init(test_init);

1.4 模块卸载函数

        模块卸载函数是模块卸载时,需要执行的函数。

        module_exit(test_exit);

1.5 模块许可声明

        模块许可声明表示模块受内核支持的程度。有许可权的模块会更受开发人员的重视。需要使用MODULE_LICENSE表示模块的许可权限。内核可以识别的许可权限如下:

        如果一个模块没有包含许可权,那么就会认为是不符合规范的。这时,内核加载这种模块时,会收到内核加载了一个非标准模块的警告。

2 模块参数和模块之间的通信

        为了增加模块的灵活性,可以给模块添加参数。模块参数可以控制模块的内部逻辑,从而使模块可以在不同的情况下完成不同的功能。

2.1 模块参数

2.1.1 用户空间的应用程序可以接受用户的参数,设备驱动程序有时候也需要接受参数。例如一个模块可以实现两种相似的功能,这是可以传递一个参数到驱动模块,以决定其使用哪一种功能。参数需要在加载模块时指定,例如insmod xxx.ko param=1。

2.1.2可以用“mdule_param(参数名,参数数据类型,参数读写权限)”来为模块定义参数。例如下列代码定义了一个长整型和整形参数:

static long a = 1;

static int b = 1;

module_param(a, long, S_IRUGO);

module_param(b, int, S_IRUGO);

        参数的数据类型可以是byte、short、ushort、int、uint、long、ulong、bool、charp(字符指针类型)。模块参数的类型中没有浮点类型。这是因为内核并不完美支持浮点数操作。在内核中使用浮点数时,除了要人工保存和恢复浮点寄存器外还有一些琐碎的事情要做。printk()函数也不支持浮点类型。

3 模块的文件格式ELF

        了解模块以何种格式存储在硬盘中,对于理解模块间怎样通信是非常有必要的。使用flie命令可以知道hello.ko模块使用的是ELF文件格式,命令如下:

        ELF文件的基本结构:

        ELF Header头位于文件的最前部。其包含了描述整个文件的基本属性,例如ELF文件版本、目标机器型号、程序入口地址等。

.text表示代码段,存放文件的代码部分。

.data表示数据段,存放已经初始化的数据等。

.Section Table表描述了ELF文件包含的所有段的信息,例如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。

.symtab表示符号表。符号表是一种映射函数到真实内存地址的数据结构。其就像一个字典,记录了在编译阶段,无法确定地址的函数。该符号表将在模块文件加载阶段,由系统赋予真实的内存地址。

4 模块之间的通信

        模块是为了完成某种特定任务而设计的。其功能比较单一,为了丰富系统的功能,模块之间常常进行通信。他们之间可以共享变量,数据结构,也可以调用对方提供的功能函数。

下图描述了模块1是怎样调用模块2的功能函数的。

        模块2的加载过程如下所示:

        1)使用insmod命令加载模块2.ko。

        2)内核为为模块2分配空间,然后将模块的代码和数据装入分配的内存中。

        3)内核发现符号表中有函数1,函数2可以导出,于是将其内存地址记录在内核符号表中。

      模块1在加载进内核时,系统会执行以下操作:

        1)insmod命令为内核分配空间,然后将模块的代码和数据装入内存中。

        2)内核在模块1的符号表(symtab)中发现一些未解析的函数。这些未解析的函数是“函数1”,“函数2”,这些函数位于模块2的代码中。所以模块1会通过内核符号表,查到相应的函数,并将函数地址填到模块1的符号表中。

        通过模块1加载的过程后,模块1可以使用模块2提供的“函数1”和“函数2”了。

        下一节《模块之间的通信实例》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值