通过《手把手教Linux驱动1-模块化编程,玩转module》的学习,我们已经掌握了如何向内核加载一个模块,现在我们学习模块之间如何传递参数。

一、给模块传递参数

当我们加载一个模块到Linux内核的时候,Linux内核允许向这个模块传递一些参数。这样设计的好处就是,让我们的模块操作起来更灵活,我们可以通过给它传递不同的参数来完成不同的功能。例如:我们写一个模块程序,来完成硬件中断的操作。在Linux操作系统中,每个中断都有一个中断号。如果我们在模块里面将中断号写死,那我们的模块只能响应特定的中断了。如果我们把中断号作为参数传递给我们的模块,那么我们的模块就可以完成对不同的中断进行操作。

那怎么向模块传递参数呢?很简单,Linux内核都给我们做好了,我们只需调用相应的接口就可以了。

(1)在模块里面,声明一个变量(全局变量),用来接收用户加载模块时传递的参数

文件位置如下:

linux-3.14\include\linux\ Moduleparam.h

函数原型:

module_param(name,type,perm)

参数:
@name用来接收参数的变量名
@type参数的数据类型
bool :布尔类型
invbool:颠倒了值的bool类型;
charp :字符指针类型,内存为用户提供的字符串分配;
int :整型
long :长整型
short :短整型
uint :无符号整型
ulong :无符号长整型
ushort :无符号短整型
@perm 指定参数访问权限。

每个模块的参数,最后都会表现在sysfs文件系统中,也就是说最后会在系统的

/sys/module/模块名字/parameters/路径下看到以参数名命名的文件。这个文件的权限就是这里指定的权限。如果perm的值为0,则在sysfs文件系统中不会生成参数对应的文件。

典型使用案列:

我们可以在模块(test.ko)里面写如下代码,接收用户加载模块时传递的参数

static unsigned int var=0;
module_param(var,uint,0400);

在加载模块的时候,传递参数:

insmod test.ko var=

最后模块里面的全局变量var的值就为100了。

如果我要传递一个字符串到模块里面,该怎么操作呢?

static char *string;
module_param(string,charp,);

在加载模块的时候,传递参数:

insmod test.ko string="yikoulinux";

有人可能会问,这段代码是不是有bug,因为你的string指针是一个野指针。

其实内核会自动给用户传递的字符串分配空间的,然后用string指针保存字符串所在内存的首地址。

(2)让模块内部变量的名字和加载模块时传递的参数名不同

函数原型:

module_param_named(name_out,name_in,type,perm);
参数:
  @name_out在加载模块时,参数的名字
  @name_in模块内部变量的名字
  @type 参数类型
  @perm 访问权限

典型使用案列:

static int var =;module_param_named(var_out,var,int,);

在加载模块的时候,传递参数:

insmod test.ko var_out=

var_out就是模块变量var在外部的名字,此时var的值为100

(3)加载模块的时候,传递字符串到模块的一个全局字符数组里面

函数原型:

module_param_string(name,string,len,perm);
参数:
  @name在加载模块时,参数的名字
  @string模块内部字符数组的名字
  @len 模块内部字符数组的大小
  @perm 访问权限

典型使用案列:

static int buffer[LEN];
module_param_string(buffer_out,buffer,LEN,);

在加载模块的时候,传递参数:

insmod test.ko buffer_out="hello word"

加载模块的时候,内核直接把"hello word"字符串口拷贝到buffer数组中。
(4)加载模块的时候,传递参数到模块的数组中

函数原型:

module_param_arry(name,type,num_point,perm);
参数:
  @name 模块的数组名,也是外部指定的参数名
  @type模块数组的数据类型
  @num_point用来获取用户在加载模块时传递的参数个数,NULL:不关心用户传递的参数个数
  @perm 访问权限

典型使用案列:

static int my_arry[];
int num;
module_param_arry(my_arry,int,&num,);

在加载模块的时候,传递参数:(多个参数以”,“隔开)

insmod test.ko my_arr=,,

注意:以上接口在调用的时候,perm指定的权限不能让普通用户具有写权限,否则编译会报错

(5)给模块里面每个接收用户参数的变量指定一个描述信息函数原型:

MODULE_PARM_DESC(name,describe);
参数
  @name 变量名
  @describe描述信息的字符串

下面我们一起来实现以下吧:

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

static int var1 = ;
module_param(var1,int,);
MODULE_PARM_DESC(var1, "Get value from user.\n");

static int var2 = ;
module_param_named(var2_out,var2,int,);
MODULE_PARM_DESC(var2, "Test var2 named var2_out.\n");


static char *string = NULL;
module_param(string,charp,);
MODULE_PARM_DESC(string, "Test module param string.\n");

static char buffer[];
module_param_string(buffer,buffer,sizeof(buffer),);
MODULE_PARM_DESC(buffer, "Test module param string.\n");

static int myarray[];
int num;
module_param_array(myarray,int,&num,);
MODULE_PARM_DESC(myarray, "Test module param array.\n");

static int __init hello_init(void)
{
int i = ;
printk("-----------------------------\n");
printk("var1 : %d\n",var1);
printk("var2 : %d\n",var2);
printk("string : %s\n",string);
printk("buffer : %s\n",buffer);

for(i=;i<num;i++)
{
printk("myarray[%d] : %d\n",i,myarray[i]);
}
printk("-----------------------------\n");
return ;
}
static void __exit hello_exit(void)
{
printk("hello_exit \n");
return;
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yikoulinux");
module_init(hello_init);
module_exit(hello_exit);

insmod hello.ko var1=100 var2_out=200 string="yikoulinux" buffer="yikoupeng" myarray=100,200,300

测试结果如下:

同时在目录/sys/module/hello/parameters下回生成对应的文件节点,文件权限与代码中定义的是一一对应的:

所有节点的内容可以用cat命令查看

切换到管理员模式,可以通过echo命令修改有W权限的文件:

[注意]操作之前一口君已经切换到管理员模式。

模块符号导出

(1)什么是符号?

这里的符号主要指的是全局变量和函数。

(2)为什么要导出符号?

Linux内核采用的是以模块化形式管理内核代码。内核中的每个模块相互之间是相互独立的,也就是说A模块的全局变量和函数,B模块是无法访问的。

有些时候,我们写一些模块代码的时候,发现部分函数功能别人已经实现了,此时我们就想如果我们可以调用他们已经实现好的函数接口就好了。那如何才能做到这点呢?符号导出了,也就是说你可以把你实现的函数接口和全局变量导出,以供其他模块使用。

在Linux内核的世界里,如果一个模块已经以静态的方式编译进的内核,那么它导出的符号就会出现在全局的内核符号表中。在Ubuntu 14.04系统中,Linux内核的全局符号表在以下文件中存放:

/usr/src/linux-headers-3.2.--generic-pae/Module.symvers

如果打开这个文件,可以发现里面的内容就是:

Addr------à符号名------à模块名------à导出符号的宏

(3)如何导出符号?

Linux内核给我们提供了两个宏:

EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);

上面宏定义的任一个使得给定的符号在模块外可用.GPL版本的宏定义只能使符号对GPL许可的模块可用.符号必须在模块文件的全局部分输出,在任何函数之外,因为宏定义扩展成一个特殊用途的并被期望是全局存取的变量的声明。

(4)模块编译时,如何寻找使用的符号?

  1. 在本模块中符号表中,寻找符号(函数或变量实现)
  2. 在内核全局符号表中寻找
  3. 在模块目录下的Module.symvers文件中寻找

如果在这三个地方都没有找到,则编译保存

三、案例演示

模块A导出全局变量global_var和函数show两个符号供模块B使用。

模块A

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

static int global_var = ;
static void show(void)
{
printk("show(): global_var =%d \n",global_var);
}
static int hello_init(void)
{
printk("module b :global_var=%d\n",global_var);
return ;
}
static void hello_exit(void)
{
printk("hello_exit \n");
return;
}EXPORT_SYMBOL(global_var);
EXPORT_SYMBOL(show);
MODULE_AUTHOR("PENG");
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);

模块B

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

extern int global_var;
extern void show(void);
static int hello_init(void)
{
printk("module a: global_var= %d\n",global_var);
show();
return ;
}
static void hello_exit(void)
{
printk("hello_exit \n");
return;
}
MODULE_AUTHOR("yikoulinux");
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);

调试步骤:

  1. 编译模块A,然后加载模块A,在模块A编译好后,在它的当前目录会看到一个Module.symvers文件,这里存放的就是我们模块A导出的符号。

  2. 将模块A编译生成的Module.symvers文件拷贝到模块B目录下,然后编译模块B,加载模块B。

  3. 通过dmesg查看模块打印的信息。

打印信息如下:

更多文章可以关注公众号:一口Linux

本文相关文章:

1 手把手教Linux驱动1-模块化编程,玩转module

手把手教Linux驱动2-之模块参数和符号导出的更多相关文章

  1. 手把手教Linux驱动1-模块化编程,玩转module

    大家好,从本篇起,一口君将手把手教大家如何来学习Linux驱动,预计会有20篇关于驱动初级部分知识点.本专题会一直更新,有任何疑问,可以留言或者加我微信. 一.什么是模块化编程? Linux的开发者, ...

  2. 手把手教Linux驱动3-之字符设备架构详解,有这篇就够了

    一.Linux设备分类 Linux系统为了管理方便,将设备分成三种基本类型: 字符设备 块设备 网络设备 字符设备: 字符(char)设备是个能够像字节流(类似文件)一样被访问的设备,由字符设备驱动程 ...

  3. Linux设备驱动程序 之 模块参数

    模块支持参数的方法 内核允许驱动程序指定参数,这些参数可在运行insmod或者modprobe命令装载模块时赋值,modprobe还可以从它的配置文件(/etc/modporb.conf)中读取参数值 ...

  4. linux驱动: 如何向模块传递参数, module_param和module_param_array

    如何向模块传递参数,Linux kernel 提供了一个简单的框架.    1.  module_param(name, type, perm); name 既是用户看到的参数名,又是模块内接受参数的 ...

  5. linux驱动之模块化驱动Makefile

    本文摘自http://blog.csdn.net/lufeiop02/article/details/6446343 Linux驱动一般以模块module的形式来加载,首先需要把驱动编译成模块的形式. ...

  6. Linux 驱动框架---模块参数

    Linux 模块的参数 通过在内核模块中定义模块参数从而可以在安装模块时通过insmod module_name paramname=param形式给模块传递参数.如果安装模块是传参数则将使用模块内定 ...

  7. Linux环境搭建 | 手把手教你安装Linux虚拟机

    前言 作为一名Linux工程师,不管是运维.应用.驱动方向,在工作中肯定会需要Linux环境.想要获得Linux环境,一个办法就是将电脑系统直接换成Linux系统,但我们平常用惯了Windows系统, ...

  8. 嵌入式linux驱动开发之给linux系统添加温度传感器模块

    忙了几天,终于可以让ds18b20在自己的开发板的linux系统上跑了!虽然ds18b20不是什么新鲜玩意,但是想想知己可以给linux系统添加模块了还是有点小鸡冻呢! 虽然说现在硬件的资源非常丰富而 ...

  9. 一步一步实现Linux设备驱动的Helloworld模块

    学了那么多程序语言,总是有一个Hello world开头,不禁感叹Hello world的强大.呵呵,废话少说,咋们的故事当然要从这个Hello world开始. 先查看自己OS使用的内核版本[don ...

随机推荐

  1. ImportError: /lib64/libm.so.6: version `CXXAB_1.3.8.' not found (required by /usr/local/python37/lib/python3.7/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so)

    问题背景 使用在AI项目中,由于需要用到tensorflow,scipy,sklearn等这些库,所以需要libstdc++库. 问题原因 这个问题的出现与写的代码无关,只与操作系统的libstdc+ ...

  2. ROS 机器人技术 - 广播与接收 TF 坐标

    上次我们学习了 TF 的基本概念和如何发布静态的 TF 坐标: ROS 机器人技术 - TF 坐标系统基本概念 ROS 机器人技术 - 静态 TF 坐标帧 这次来总结下如何发布一个自定义的 TF 坐标 ...

  3. sourceTree安装、跳过bitbucket注册免登陆方法

    下载好以后,点击安装运行,会出现下面这个窗口 关掉这个窗口,打开C:\Users\{users}\AppData\Local\Atlassian\SourceTree(users是计算机的名字),新建 ...

  4. 前端网(http://www.qdfuns.com/)不能访问了

    前端网(http://www.qdfuns.com/)不能访问了 之前写的一些知识点也找不到了,有点难受.... 这说明知识点还是放在本地电脑稳一点,多备份,云端时刻在变化... 希望博客园别也用着用 ...

  5. __slots__属性

    使用__slots__时,子类不受影响 class Person(object): __slots__ = ("name","age") def __str__ ...

  6. HDU 1756 Cupid's Arrow 计算几何 判断一个点是否在多边形内

    LINK:Cupid's Arrow 前置函数 atan2 返回一个向量的幅角.范围为[Pi,-Pi) 值得注意的是 返回的是 相对于x轴正半轴的辐角. 而判断一个点是否在一个多边形内 通常有三种方法 ...

  7. 4.23 子串 AC自动机 概率期望 高斯消元

    考虑40分. 设出状态 f[i]表示匹配到了i位还有多少期望长度能停止.可以发现这个状态有环 需要高斯消元. 提供一种比较简单的方法:由于期望的线性可加性 可以设状态f[i]表示由匹配到i到匹配到i+ ...

  8. 解决痛苦的方法/cy

    这篇文章 源于我所有痛苦的回忆. 由于痛苦太多了 体会完了 所以 觉得这些都不算是什么大问题了 所以 这里 是解决痛苦的方法. 真的很痛苦的话 可以这样 对着全班人喊你们 都没我强 因为 你们都没有我 ...

  9. 死磕abstractqueuedsynchronizer源码

    第一次写博客,先练练手. 1.AQS是什么? 在Lock中,用到了一个同步队列AQS,全称为AbstractQueuedSynchronizer,它是一个同步工具也是lock用来实现线程同步的核心组件 ...

  10. 埋在MySQL数据库应用中的17个关键问题!

    作者:扎瓦陈序元 来源:https://blog.csdn.net/weixin_42882439 MySQL的使用非常普遍,跟MySQL有关的话题也非常多,如性能优化.高可用性.强一致性.安全.备份 ...