内核模块参数和依赖

一、模块传参

module_param(name,type,perm);//将指定的全局变量设置成模块参数
/*
name:全局变量名
type:
    使用符号      实际类型                传参方式
	bool	     bool           insmod xxx.ko  变量名=0 或 1
	invbool      bool           insmod xxx.ko  变量名=0 或 1
	charp        char *         insmod xxx.ko  变量名="字符串内容"
	short        short          insmod xxx.ko  变量名=数值
	int          int            insmod xxx.ko  变量名=数值
	long         long           insmod xxx.ko  变量名=数值
	ushort       unsigned short insmod xxx.ko  变量名=数值
	uint         unsigned int   insmod xxx.ko  变量名=数值
	ulong        unsigned long  insmod xxx.ko  变量名=数值
perm:给对应文件 /sys/module/name/parameters/变量名 指定操作权限
	#define S_IRWXU 00700
	#define S_IRUSR 00400
	#define S_IWUSR 00200
	#define S_IXUSR 00100
	#define S_IRWXG 00070
	#define S_IRGRP 00040
	#define S_IWGRP 00020
	#define S_IXGRP 00010
	#define S_IRWXO 00007
	#define S_IROTH 00004
	#define S_IWOTH 00002  //不要用 编译出错
	#define S_IXOTH 00001
*/
module_param_array(name,type,&num,perm);
/*
name、type、perm同module_param,type指数组中元素的类型
&num:存放数组大小变量的地址,可以填NULL(确保传参个数不越界)
    传参方式 insmod xxx.ko  数组名=元素值0,元素值1,...元素值num-1  
*/

可用MODULE_PARAM_DESC宏对每个参数进行作用描述,用法:

MODULE_PARM_DESC(变量名,字符串常量);

字符串常量的内容用来描述对应参数的作用

modinfo可查看这些参数的描述信息

示例:
testparam.c

#include 
#include 

int gt = 10;
char *gstrr = "hello";
int garr[5] = {1,2,3,4,5};

module_param(gt, int, 0664);
module_param(gstrr, charp, 0664);
module_param_array(garr, int, NULL, 0664);

/* 入口 */
int __init testparam_init(void)
{
	int i=0;
	printk("gt = %d\n", gt);
	printk("gstrr = %s\n", gstrr);
	
	for(i = 0; i < 5; i++) {
		printk("%d ", garr[i]);
	}
	printk("\n");

	return 0;
}

/* 出口 */
void __exit testparam_exit(void)
{
	printk("testparam will exit\n");
}

MODULE_LICENSE("GPL");
module_init(testparam_init);
module_exit(testparam_exit);

内核模块参数和依赖_第1张图片

二、模块依赖

​ 既然内核模块的代码与其它内核代码共用统一的运行环境,也就是说模块只是存在形式上独立,运行上其实和内核其它源码是一个整体,它们隶属于同一个程序,因此一个模块或内核其它部分源码应该可以使用另一个模块的一些全局特性。

一个模块中这些可以被其它地方使用的名称被称为导出符号,所有导出符号被填在同一个表中这个表被称为符号表。

最常用的可导出全局特性为全局变量和函数

查看符号表的命令:nm
nm查看elf格式的可执行文件或目标文件中包含的符号表,用法:

nm 文件名 (可以通过man nm查看一些字母含义)

两个用于导出模块中符号名称的宏:

EXPORT_SYMBOL(函数名或全局变量名)
EXPORT_SYMBOL_GPL(函数名或全局变量名)   需要GPL许可证协议验证

使用导出符号的地方,需要对这些符号进行extern声明后才能使用这些符号

B模块使用了A模块导出的符号,此时称B模块依赖于A模块,则:

  1. 编译次序:先编译模块A,再编译模块B,当两个模块源码在不同目录时,需要:i. 先编译导出符号的模块A ii. 拷贝A模块目录中的Module.symvers到B模块目录 iii. 编译使用符号的模块B。否则编译B模块时有符号未定义错误
  2. 加载次序:先插入A模块,再插入B模块,否则B模块插入失败
  3. 卸载次序:先卸载B模块,在卸载A模块,否则A模块卸载失败

补充说明
内核符号表(直接当文本文件查看)

   /proc/kallsyms运行时    /boot/System.map编译后

在同一目录下

modulea.c

#include 
#include 

int gt = 10; // 全局变量

EXPORT_SYMBOL(gt); // 

int __init modulea_init(void)
{
	printk("modulea_init gt = %d\n", gt);

	return 0;
}

void __exit modulea_exit(void)
{
	printk("modulea will exit\n");
}

MODULE_LICENSE("GPL");
module_init(modulea_init);
module_exit(modulea_exit);

moduleb.c

#include 
#include 

extern int gt; //声明gt变量 

int __init moduleb_init(void)
{
	printk("moduleb_init gt = %d\n", gt);

	return 0;
}

void __exit moduleb_exit(void)
{
	printk("moduleb will exit\n");
}

MODULE_LICENSE("GPL");
module_init(moduleb_init);
module_exit(moduleb_exit);

Makefile

ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)
KERNELDIR ?= /home/myubuntu/Linux_4412/kernel/linux-3.14 
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

obj-m += modulea.o # 注意编译顺序
obj-m += moduleb.o
endif

不在同目录下

/module_a/module_a.c

#include 
#include 

int gt = 10;

EXPORT_SYMBOL(gt); 

int __init module_a_init(void)
{
	printk("module_a_init gt = %d\n", gt);

	return 0;
}

void __exit module_a_exit(void)
{
	printk("module_a will exit\n");
}

MODULE_LICENSE("GPL");
module_init(module_a_init);
module_exit(module_a_exit);

/module_a/Makefile

ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)
KERNELDIR ?= /home/myubuntu/Linux_4412/kernel/linux-3.14 
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

obj-m += module_a.o
endif

/module_b/module_b.c

#include 
#include 

extern int gt;

int __init module_b_init(void)
{
	printk("module_b_init gt = %d\n", gt);

	return 0;
}

void __exit module_b_exit(void)
{
	printk("module_b will exit\n");
}

MODULE_LICENSE("GPL");
module_init(module_b_init);
module_exit(module_b_exit);

/module_b/Makefile

ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)
KERNELDIR ?= /home/myubuntu/Linux_4412/kernel/linux-3.14 
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

obj-m += module_b.o
endif

内核模块参数和依赖_第2张图片

你可能感兴趣的:(驱动开发,linux)