【2】模块参数与模块之间的通信

模块参数

我们在运行用户空间的程序的时候可以接参数,驱动程序也可以接参数运行。参数在加载模块的时候指定,在模块代码中用module_param(参数名,参数类型,参数读写权限)来为模块定义参数。具体用法看代码如下:

#include <linux/init.h>
#include <linux/module.h>

static char* str = "hello";
static int value = 1;

static int hello_init(void) {
    printk(KERN_EMERG "hello_init str: %s\n", str);
    return 0;
}

static void hello_exit(void) {
    printk(KERN_EMERG "hello_exit value: %d\n", value);
}

module_param(value, int, 0644);
module_param(str, charp, 0644);

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

上面的驱动在加载的时候就可以将a的值通过参数来指定,命令如下

sudo insmod hello.ko value=5 str="vegeta"

这样在加载模块时就会打印str=vegeta,卸载模块时打印value=5。
在这里插入图片描述

sysfs
sysfs是内核给资源创建的目录或者文件,模块会在/sys/module下创建一个跟模块名称一样的目录,目录在模块卸载的时候也会被一并删除掉。
例如上述hello模块加载之后,就会生成/sys/module/hello目录,在其中存放hello模块的资源信息。
上述模块传参中使用的str/value,会在/sys/module/hello/parameters目录下创建两个文件,跟参数名称一致
在这里插入图片描述
str和value中存放的的内容跟我们传参的内容是一致的
在这里插入图片描述
我所使用的版本,vim是可以修改value和str的内容的,并且是能生效的。例如当你修改了value之后,卸载模块打印的就是你修改之后的值了。


模块的文件格式

通过命令file hello.ko来查看模块是以何种格式存储在硬盘中。基本可以分为如下几个结构

名称释义
ELF Header描述整个文件的基本属性
.text代码段,存放文件的代码部分
.data数据段,存放已经初始化的数据等
.Section Table描述了ELF文件包含的所有段的信息
.symtab符号表,映射函数到真实内存地址的数据结构,就像一个字典,记录了在编译阶段无法确定地址的函数,在模块加载的时候由系统赋予真实的地址

模块通信(符号导出)

需要实现2个模块,目标是模块1(add_sub.ko)提供函数供模块2(hello.ko)来进行调用。
具体实现方式是将模块1的符号表提供给模块2,从而使得模块2能调用到模块1的函数和变量等。

模块(add_sub.ko)代码

/*模块1代码 add_sub.c,提供2个函数供其他模块调用*/
#include <linux/module.h>
#include <linux/init.h>

int add_integer(int value1, int value2) {
    printk(KERN_EMERG "add_integer value1:%d value2:%d \n", value1, value2);
	return value1 + value2;
}

int sub_integer(int value1, int value2) {
    printk(KERN_EMERG "sub_integer value1:%d value2:%d \n", value1, value2);
	return value1 - value2;
}

static int add_sub_init(void) {
    return 0;
}

static void add_sub_exit(void) {
}

EXPORT_SYMBOL(add_integer);
EXPORT_SYMBOL(sub_integer);

module_init(add_sub_init);
module_exit(add_sub_exit);

MODULE_LICENSE("GPL");

模块1提供了加和减(add_integer/sub_integer)2个函数,这2个函数需要导出到内核符号表,才能被其他模块调用。EXPORT_SYMBOL()就是导出宏,就是定义函数能够被其他模块调用到。要注意的是内核函数很多,不能出现重名。但是编译器认为所有模块的函数都是私有的,不同模块之间出现重名不影响编译,所以如果只是模块自己内部使用的话不需要担心重名问题。


模块2(hello.ko)代码

/*模块2代码 hello.c,调用模块1的函数*/
/*模块2代码 hello.c,调用模块1的函数*/
#include <linux/module.h>
#include <linux/init.h>

static int value1 = 2;
static int value2 = 1;

extern int add_integer(int value1, int value2);
extern int sub_integer(int value1, int value2);

static int hello_init(void) {
	int result = add_integer(value1, value2); /*调用模块1的函数*/
	printk(KERN_EMERG "hello_init value1:%d value2:%d result: %d\n", value1, value2, result);
	return 0;
}

static void hello_exit(void) {
	int result = sub_integer(value1, value2); /*调用模块1的函数*/
	printk(KERN_EMERG "hello_exit value1:%d value2:%d result: %d\n", value1, value2, result);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

模块2在加载的时候调用模块1的加法,卸载的时候调用模块2的减法。


模块通信的编译
add_sub模块的编译的Makefile与上一篇的hello world程序没有差异,直接可用。但是hello模块的Makefile需要修改一下,加上KBUILD_EXTRA_SYMBOLS否则会报错找不到add_sub两个函数的实现。Makefile如下:

ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
KBUILD_EXTRA_SYMBOLS += ./Modules.symvers
export KBUILD_EXTRA_SYMBOLS

all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	rm -rf *.o *~ codre .depend .*.cmd *.ko *.mod *.mod.c *.tmp_versions *.order *.symvers .cache.mk .tmp_versions
endif

编译的步骤为:

  1. 编译add_sub模块
  2. 将add_sub模块生成的Module.symvers复制到hello模块Makefile同级目录下
  3. 编译hello模块

模块通信测试

在加载的时候需要先加载add_sub.ko再加载hello.ko。在hello.ko加载的时候调用add_sub.ko中的加法,卸载的时候调用add_sub.ko中的减法。输出log如下
模块通信Log输出

模块的加载过程解释

模块1(add_sub.ko)的加载过程:
1、内核为模块1分配空间,将模块的代码和数据装入分配的内存中
2、内核发现模块1的符号表中有函数可以导出,就将其内存地址记录在内核的符号表中

模块2(hello.ko)的加载过程:
1、内核为模块2分配空间,将模块的代码和数据装入分配的内存中
2、内核在模块2的符号表中发现一些未解析的函数。所以查找内核符号表找到对应的函数,将函数的地址填到模块2的符号表中,这个地址就是模块1中的函数地址。
通过上述的操作,就可以实现模块之间函数的调用

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值