Linux内核与驱动学习记录-内核模块传参和符号共享
 2021.05.23    |      Linux MMC子系统    |     AilsonJack    |     暂无评论    |     400 views
By: Ailson Jack
Date: 2021-05-23
个人博客: http://www.only2fire.com/
<p class="artical_littlestyle1">1.内核模块传参</p><p style="text-indent: 2em;">内核模块作为一个可拓展的动态模块,为 Linux 内核提供了灵活性,但是有时我们需要根据不同的应用场景给内核模块传递不同的参数,例如在程序中开启调试模式、设置详细输出模式以及制定与具体模块相关的选项,为了满足这种需求,内核允许对内核模块指定参数,而这些参数可以在装载内核模块时改变。</p><p style="text-indent: 2em;">Linux内核提供一个宏module_param来实现模块的参数传递,module_param宏定义在include/linux/moduleparam.h文件中。module_param宏的定义形式如下:</p><pre class="brush:cpp;toolbar:false PrismJs">#define&nbsp;module_param(name,&nbsp;type,&nbsp;perm)</pre><p style="text-indent: 2em;">module_param需要三个参数:</p><p style="text-indent: 2em;">name:变量的名称;<br/></p><p style="text-indent: 2em;">type:变量的类型,目前内核支持的类型有 byte, short, ushort, int, uint, long, ulong, charp, bool,invbool。其中 charp 表示的是字符指针, bool 是布尔类型,其值只能为 0 或者是 1; invbool 是反布尔类型,其值也是只能取 0 或者是 1,但是 true 值表示 0, false 表示 1。变量是 char 类型时,传参只能是 byte,char * 时只能是 charp;</p><p style="text-indent: 2em;">perm:sysfs入口项的访问许可掩码,可选的值:S_IRUSR,S_IWUSR,S_IRGRP,S_IWGRP,S_IROTH,S_IXOTH,S_IRUGO,S_IWUGO(这些值可以通过或的方式进行组合,比如S_IRUSR | S_IWUSR表示用户拥有读写权限)。对于这些宏的定义可以参考文件:include/linux/stat.h。<br/></p><p style="text-indent: 2em;">上述文件权限唯独没有关于可执行权限的设置,请注意,该文件不允许它具有可执行权限。如果强行给该参数赋予表示可执行权限的参数值 S_IXUGO,那么最终生成的内核模块在加载时会提示错误。</p><p style="text-indent: 2em;">这里列举一个简单的内核模块参数传递的例子,module_param.c文件内容:<br/></p><pre class="brush:cpp;toolbar:false PrismJs">/** &nbsp;*&nbsp;@file&nbsp;module_param.c &nbsp;*&nbsp;@author&nbsp;Ailson&nbsp;Jack&nbsp;(jackailson@foxmail.com) &nbsp;*&nbsp;@brief &nbsp;*&nbsp;@version&nbsp;1.0 &nbsp;*&nbsp;@date&nbsp;2021-05-22 &nbsp;* &nbsp;*&nbsp;@copyright&nbsp;Copyright&nbsp;(c)&nbsp;2021 &nbsp;* &nbsp;*&nbsp;@note&nbsp;blog:www.only2fire.com &nbsp;* &nbsp;*/ #include&nbsp;&lt;linux/init.h&gt; #include&nbsp;&lt;linux/module.h&gt; #include&nbsp;&lt;linux/kernel.h&gt; static&nbsp;int&nbsp;int_type&nbsp;=&nbsp;0; module_param(int_type,&nbsp;int,&nbsp;0); static&nbsp;bool&nbsp;bool_type&nbsp;=&nbsp;0; module_param(bool_type,&nbsp;bool,&nbsp;0644); static&nbsp;char&nbsp;char_type&nbsp;=&nbsp;0; module_param(char_type,&nbsp;byte,&nbsp;0); static&nbsp;char&nbsp;*str_type&nbsp;=&nbsp;0; module_param(str_type,&nbsp;charp,&nbsp;S_IRUGO&nbsp;|&nbsp;S_IWUSR); /*&nbsp;内核模块加载函数&nbsp;*/ static&nbsp;int&nbsp;__init&nbsp;module_param_init(void) { &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;Module&nbsp;Param&nbsp;init!\r\n&quot;); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;int_type=%d\r\n&quot;,&nbsp;int_type); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;bool_type=%d\r\n&quot;,&nbsp;bool_type); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;char_type=%d\r\n&quot;,&nbsp;char_type); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;str_type=%s\r\n&quot;,&nbsp;str_type); &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0; } /*&nbsp;内核模块卸载函数&nbsp;*/ static&nbsp;void&nbsp;__exit&nbsp;module_param_exit(void) { &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;Module&nbsp;Param&nbsp;exit!\r\n&quot;); } module_init(module_param_init); module_exit(module_param_exit); MODULE_LICENSE(&quot;GPL&nbsp;v2&quot;);&nbsp;//表示模块代码接受的软件许可协议 MODULE_AUTHOR(&quot;Ailson&nbsp;Jack&quot;);&nbsp;//描述模块的作者信息 MODULE_DESCRIPTION(&quot;module&nbsp;param&quot;);&nbsp;//对模块的简单介绍 MODULE_ALIAS(&quot;module_param&quot;);&nbsp;//给模块设置一个别名</pre><p style="text-indent: 2em;">Makefile文件内容:</p><pre class="brush:cpp;toolbar:false PrismJs">#&nbsp;指向编译出来的&nbsp;linux&nbsp;内核具体路径 KERNEL_DIR&nbsp;=&nbsp;../../kernel/ebf-buster-linux/build_image/build #&nbsp;定义变量,并且导出变量给子&nbsp;Makefile&nbsp;使用 ARCH&nbsp;=&nbsp;arm CROSS_COMPILE&nbsp;=&nbsp;arm-linux-gnueabihf- export&nbsp;ARCH&nbsp;CROSS_COMPILE #&nbsp;obj-m&nbsp;:=&nbsp;&lt;模块名&gt;.o:&nbsp;定义要生成的模块 obj-m&nbsp;:=&nbsp;module_param.o #&nbsp;选项&nbsp;&quot;-C&quot;:让&nbsp;make&nbsp;工具跳转到&nbsp;linux&nbsp;内核目录下读取顶层&nbsp;Makefile #&nbsp;&quot;M=&quot;&nbsp;表示内核模块源码目录 #&nbsp;$(CURDIR):&nbsp;Makefile&nbsp;默认变量,值为当前目录所在路径 #&nbsp;make&nbsp;modules:&nbsp;执行&nbsp;Linux&nbsp;顶层&nbsp;Makefile&nbsp;的伪目标,它实现内核模块的源码读取并编译为.ko文件 all: &nbsp;&nbsp;&nbsp;&nbsp;$(MAKE)&nbsp;-C&nbsp;$(KERNEL_DIR)&nbsp;M=$(CURDIR)&nbsp;modules .PHONY:clean&nbsp;copy clean: &nbsp;&nbsp;&nbsp;&nbsp;$(MAKE)&nbsp;-C&nbsp;$(KERNEL_DIR)&nbsp;M=$(CURDIR)&nbsp;clean copy: &nbsp;&nbsp;&nbsp;&nbsp;cp&nbsp;*.ko&nbsp;/home/ailsonjack/share/nfs/temp</pre><p style="text-indent: 2em;">编译module_param内核模块,将生成的module_param.ko文件通过nfs或者scp拷贝到开发板,然后在开发板中执行命令:</p><pre class="brush:plain;toolbar:false PrismJs">sudo&nbsp;insmod&nbsp;module_param.ko&nbsp;int_type=12&nbsp;bool_type=1&nbsp;char_type=201&nbsp;str_type=&quot;hello!&quot;</pre><p style="text-align:center"><img src="/uploads/AilsonJack/2021.05.23/200823892771688.png" onclick="preview_image(&#39;/uploads/AilsonJack/2021.05.23/200823892771688.png&#39;)"/></p><p style="text-indent: 2em;">我们定义的四个模块参数,会在&#39;/sys/module/模块名/parameters&#39;下存在以模块参数为名的文件。由于 int_type 和 char_type 的权限是 0,所以我们没有权限查看该参数。</p><p style="text-align:center"><img src="/uploads/AilsonJack/2021.05.23/200823893593251.png" onclick="preview_image(&#39;/uploads/AilsonJack/2021.05.23/200823893593251.png&#39;)"/></p><p class="artical_littlestyle2">2.符号共享</p><p style="text-indent: 2em;">符号共享是指内核模块能够使用其他内核模块导出的符号,或者内核模块将自己模块内的符号导出给其他内核模块使用。这里的符号指的是内核模块中导出的函数或者变量,在加载模块时被记录在公共内核符号表中,以供其他模块调用。这个机制,允许我们使用分层的思想解决一些复杂的模块设计。我们在编写一个驱动的时候,可以把驱动按照功能分成几个内核模块,借助符号共享去实现模块与模块之间的接口调用,变量共享。</p><p style="text-indent: 2em;">通常情况下我们无需导出任何符号,但是如果其他模块想要从我们这个模块中获取某些符号的时候,就可以考虑导出符号为其提供服务,这被称为模块层叠技术。例如 msdos 文件系统依赖于由 fat 模块导出的符号;USB 输入设备模块层叠在 usbcore 和 input 模块之上。也就是我们可以将模块分为多个层,通过简化每一层来实现复杂的项目。</p><p style="text-indent: 2em;">如果一个模块需要向其他模块导出符号,则应该使用下面的宏:</p><pre class="brush:cpp;toolbar:false PrismJs">EXPORT_SYMBOL(name); EXPORT_SYMBOL_GPL(name);</pre><p style="text-indent: 2em;">符号必须在模块文件的全局部分导出,不能在函数中使用,EXPORT_SYMBOL_GPL使得导出的模块只能被 GPL 许可的模块使用。</p><p style="text-indent: 2em;">这里使用内核模块传参小节的module_param.c文件为基础进行修改,作为一个导出内核模块参数的内核模块,module_param.c文件的内容如下:</p><pre class="brush:cpp;toolbar:false PrismJs">/** &nbsp;*&nbsp;@file&nbsp;module_param.c &nbsp;*&nbsp;@author&nbsp;Ailson&nbsp;Jack&nbsp;(jackailson@foxmail.com) &nbsp;*&nbsp;@brief &nbsp;*&nbsp;@version&nbsp;1.0 &nbsp;*&nbsp;@date&nbsp;2021-05-22 &nbsp;* &nbsp;*&nbsp;@copyright&nbsp;Copyright&nbsp;(c)&nbsp;2021 &nbsp;* &nbsp;*&nbsp;@note&nbsp;blog:www.only2fire.com &nbsp;* &nbsp;*/ #include&nbsp;&lt;linux/init.h&gt; #include&nbsp;&lt;linux/module.h&gt; #include&nbsp;&lt;linux/kernel.h&gt; static&nbsp;int&nbsp;int_type&nbsp;=&nbsp;0; module_param(int_type,&nbsp;int,&nbsp;0); EXPORT_SYMBOL(int_type);&nbsp;//导出&nbsp;int_type&nbsp;作为共享符号 static&nbsp;bool&nbsp;bool_type&nbsp;=&nbsp;0; module_param(bool_type,&nbsp;bool,&nbsp;0644); static&nbsp;char&nbsp;char_type&nbsp;=&nbsp;0; module_param(char_type,&nbsp;byte,&nbsp;0); static&nbsp;char&nbsp;*str_type&nbsp;=&nbsp;0; module_param(str_type,&nbsp;charp,&nbsp;S_IRUGO&nbsp;|&nbsp;S_IWUSR); int&nbsp;data_inc(int&nbsp;val) { &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(val&nbsp;+&nbsp;1); } EXPORT_SYMBOL(data_inc);&nbsp;//导出&nbsp;data_inc&nbsp;作为共享符号 int&nbsp;data_dec(int&nbsp;val) { &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(val&nbsp;-&nbsp;1); } EXPORT_SYMBOL(data_dec);&nbsp;//导出&nbsp;data_dec&nbsp;作为共享符号 /*&nbsp;内核模块加载函数&nbsp;*/ static&nbsp;int&nbsp;__init&nbsp;module_param_init(void) { &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;Module&nbsp;Param&nbsp;init!\r\n&quot;); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;int_type=%d\r\n&quot;,&nbsp;int_type); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;bool_type=%d\r\n&quot;,&nbsp;bool_type); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;char_type=%d\r\n&quot;,&nbsp;char_type); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;str_type=%s\r\n&quot;,&nbsp;str_type); &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0; } /*&nbsp;内核模块卸载函数&nbsp;*/ static&nbsp;void&nbsp;__exit&nbsp;module_param_exit(void) { &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;Module&nbsp;Param&nbsp;exit!\r\n&quot;); } module_init(module_param_init); module_exit(module_param_exit); MODULE_LICENSE(&quot;GPL&nbsp;v2&quot;);&nbsp;//表示模块代码接受的软件许可协议 MODULE_AUTHOR(&quot;Ailson&nbsp;Jack&quot;);&nbsp;//描述模块的作者信息 MODULE_DESCRIPTION(&quot;module&nbsp;param&quot;);&nbsp;//对模块的简单介绍 MODULE_ALIAS(&quot;module_param&quot;);&nbsp;//给模块设置一个别名</pre><p style="text-indent: 2em;">module_param.h文件内容:</p><pre class="brush:cpp;toolbar:false PrismJs">/** &nbsp;*&nbsp;@file&nbsp;module_param.h &nbsp;*&nbsp;@author&nbsp;Ailson&nbsp;Jack&nbsp;(jackailson@foxmail.com) &nbsp;*&nbsp;@brief &nbsp;*&nbsp;@version&nbsp;1.0 &nbsp;*&nbsp;@date&nbsp;2021-05-23 &nbsp;* &nbsp;*&nbsp;@copyright&nbsp;Copyright&nbsp;(c)&nbsp;2021 &nbsp;* &nbsp;*&nbsp;@note&nbsp;blog:www.only2fire.com &nbsp;* &nbsp;*/ #ifndef&nbsp;__MODULE_PARAM_H__ #define&nbsp;__MODULE_PARAM_H__ #ifdef&nbsp;__cplusplus extern&nbsp;&quot;C&quot;&nbsp;{ #endif&nbsp;/*&nbsp;__cplusplus&nbsp;*/ //&nbsp;下面定义的是&nbsp;module_param&nbsp;内核模块导出的变量和函数 extern&nbsp;int&nbsp;int_type; int&nbsp;data_inc(int&nbsp;val); int&nbsp;data_dec(int&nbsp;val); #ifdef&nbsp;__cplusplus } #endif&nbsp;/*&nbsp;__cplusplus&nbsp;*/ #endif&nbsp;/*&nbsp;__MODULE_PARAM_H__&nbsp;*/</pre><p style="text-indent: 2em;">symbol_share.c文件内容:</p><pre class="brush:cpp;toolbar:false PrismJs">/** &nbsp;*&nbsp;@file&nbsp;symbol_share.c &nbsp;*&nbsp;@author&nbsp;Ailson&nbsp;Jack&nbsp;(jackailson@foxmail.com) &nbsp;*&nbsp;@brief &nbsp;*&nbsp;@version&nbsp;1.0 &nbsp;*&nbsp;@date&nbsp;2021-05-23 &nbsp;* &nbsp;*&nbsp;@copyright&nbsp;Copyright&nbsp;(c)&nbsp;2021 &nbsp;* &nbsp;*&nbsp;@note&nbsp;blog:www.only2fire.com &nbsp;* &nbsp;*/ #include&nbsp;&lt;linux/init.h&gt; #include&nbsp;&lt;linux/module.h&gt; #include&nbsp;&lt;linux/kernel.h&gt; #include&nbsp;&quot;module_param.h&quot; /*&nbsp;内核模块加载函数&nbsp;*/ static&nbsp;int&nbsp;__init&nbsp;symbol_share_init(void) { &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;Symbol&nbsp;Share&nbsp;init!\r\n&quot;); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;int_type=%d\r\n&quot;,&nbsp;int_type); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;data_inc(int_type):%d\r\n&quot;,&nbsp;data_inc(int_type)); &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;data_dec(int_type):%d\r\n&quot;,&nbsp;data_dec(int_type)); &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0; } /*&nbsp;内核模块卸载函数&nbsp;*/ static&nbsp;void&nbsp;__exit&nbsp;symbol_share_exit(void) { &nbsp;&nbsp;&nbsp;&nbsp;printk(KERN_ALERT&nbsp;&quot;Symbol&nbsp;Share&nbsp;exit!\r\n&quot;); } module_init(symbol_share_init); module_exit(symbol_share_exit); MODULE_LICENSE(&quot;GPL&nbsp;v2&quot;);&nbsp;//表示模块代码接受的软件许可协议 MODULE_AUTHOR(&quot;Ailson&nbsp;Jack&quot;);&nbsp;//描述模块的作者信息 MODULE_DESCRIPTION(&quot;symbol&nbsp;share&quot;);&nbsp;//对模块的简单介绍 MODULE_ALIAS(&quot;symbol_share&quot;);&nbsp;//给模块设置一个别名</pre><p style="text-indent: 2em;">Makefile文件内容:</p><pre class="brush:plain;toolbar:false PrismJs">#&nbsp;指向编译出来的&nbsp;linux&nbsp;内核具体路径 KERNEL_DIR&nbsp;=&nbsp;../../kernel/ebf-buster-linux/build_image/build #&nbsp;定义变量,并且导出变量给子&nbsp;Makefile&nbsp;使用 ARCH&nbsp;=&nbsp;arm CROSS_COMPILE&nbsp;=&nbsp;arm-linux-gnueabihf- export&nbsp;ARCH&nbsp;CROSS_COMPILE #&nbsp;obj-m&nbsp;:=&nbsp;&lt;模块名&gt;.o:&nbsp;定义要生成的模块 obj-m&nbsp;:=&nbsp;module_param.o&nbsp;symbol_share.o #&nbsp;选项&nbsp;&quot;-C&quot;:让&nbsp;make&nbsp;工具跳转到&nbsp;linux&nbsp;内核目录下读取顶层&nbsp;Makefile #&nbsp;&quot;M=&quot;&nbsp;表示内核模块源码目录 #&nbsp;$(CURDIR):&nbsp;Makefile&nbsp;默认变量,值为当前目录所在路径 #&nbsp;make&nbsp;modules:&nbsp;执行&nbsp;Linux&nbsp;顶层&nbsp;Makefile&nbsp;的伪目标,它实现内核模块的源码读取并编译为.ko文件 all: &nbsp;&nbsp;&nbsp;&nbsp;$(MAKE)&nbsp;-C&nbsp;$(KERNEL_DIR)&nbsp;M=$(CURDIR)&nbsp;modules .PHONY:clean&nbsp;copy clean: &nbsp;&nbsp;&nbsp;&nbsp;$(MAKE)&nbsp;-C&nbsp;$(KERNEL_DIR)&nbsp;M=$(CURDIR)&nbsp;clean copy: &nbsp;&nbsp;&nbsp;&nbsp;cp&nbsp;*.ko&nbsp;/home/ailsonjack/share/nfs/temp</pre><p style="text-indent: 2em;">编译module_param内核模块和symbol_share内核模块,将生成的module_param.ko和symbol_share.ko文件通过nfs或者scp拷贝到开发板,然后在开发板中执行命令:</p><pre class="brush:plain;toolbar:false PrismJs">sudo&nbsp;insmod&nbsp;module_param.ko&nbsp;int_type=12&nbsp;bool_type=1&nbsp;char_type=201&nbsp;str_type=&quot;hello!&quot; sudo&nbsp;insmod&nbsp;symbol_share.ko</pre><p style="text-indent: 2em;">注意,要先加载module_param内核模块,再加载symbol_share内核模块,因为symbol_share内核模块会依赖module_param内核模块导出的符号,如果先加载symbol_share内核模块,symbol_share内核模块将会加载失败。</p><p style="text-align:center"><img src="/uploads/AilsonJack/2021.05.23/200823208251279.png" onclick="preview_image(&#39;/uploads/AilsonJack/2021.05.23/200823208251279.png&#39;)"/></p><p style="text-indent: 2em;">查看module_param内核模块导出的符号:</p><pre class="brush:plain;toolbar:false PrismJs">cat&nbsp;/proc/kallsyms&nbsp;|&nbsp;grep&nbsp;int_type cat&nbsp;/proc/kallsyms&nbsp;|&nbsp;grep&nbsp;data_inc cat&nbsp;/proc/kallsyms&nbsp;|&nbsp;grep&nbsp;data_dec</pre><p style="text-align:center"><img src="/uploads/AilsonJack/2021.05.23/200823577477245.png" onclick="preview_image(&#39;/uploads/AilsonJack/2021.05.23/200823577477245.png&#39;)"/></p>
欢迎关注博主的公众号呀,精彩内容随时掌握:
热情邀请仔细浏览下博客中的广告,万一有对自己有用或感兴趣的呢。◕ᴗ◕。。
如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^,当然了能够赞赏博主,那就非常感谢啦!
注: 转载请注明出处,谢谢!^_^
暂无评论,要不要来个沙发
发表评论

 
Copyright © 2015~2023  说好一起走   保留所有权利   |  百度统计  蜀ICP备15004292号