首页 资讯 查看内容

Linux干货|内核模块参数传递与sysfs

2023-07-07 13:25:26

来源: 面包芯语

一、前言

Linux应用开发中,为使应用程序更加灵活地执行用户的预期功能,我们有时候会通过命令行传递一些参数到main函数中,使得代码逻辑可以依据参数执行不同的任务。同样,Linux内核也提供了类似main函数传参的内核传参机制,编写内核程序时只要实现传参接口,用户在加载内核模块时即可传入指定参数,使得内核模块更加灵活。


(资料图)

二、内核模块传参

1、内核模块传参意义

内核模块传参会使得程序更加灵活,可以向上适配复杂的应用程序,向下兼容不同硬件设备;同时,通过参数选择,可以避免重新编译内核模块,省时省力;另外,通过内核模块传递参数也能更好地兼容和迭代产品。

2、内核传参实现

内核支持传递的参数类型包含了C语言中常用的数据类型。

实现内核模块传参,只需在内核模块程序中调用module_param系列宏即可,module_param系列宏位于“/include/linux/moduleparam.h”中定义,包括module_param_arraymodule_param_stringmodule_param_cb

#definemodule_param(name,type,perm)\module_param_named(name,name,type,perm)#definemodule_param_array(name,type,nump,perm)\module_param_array_named(name,name,type,nump,perm)#definemodule_param_string(name,string,len,perm)\staticconststructkparam_string__param_string_##name\={len,string};\__module_param_call(MODULE_PARAM_PREFIX,name,\¶m_ops_string,\.str=&__param_string_##name,perm,-1,0);\__MODULE_PARM_TYPE(name,"string")#definemodule_param_cb(name,ops,arg,perm)\__module_param_call(MODULE_PARAM_PREFIX,name,ops,arg,perm,-1,0)

module_param用于处理基本类型参数,module_param_array用于处理数组类型参数,module_param_string用于处理字符串类型参数。

1)基本类型

module_param(name,type,perm)
/*include/linux/stat.h*/#defineS_IRWXUGO(S_IRWXU|S_IRWXG|S_IRWXO)/*所有用户可读、写、执行*/#defineS_IALLUGO(S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)/*所有用户可读、写、执行*/#defineS_IRUGO(S_IRUSR|S_IRGRP|S_IROTH)/*所有用户可读*/#defineS_IWUGO(S_IWUSR|S_IWGRP|S_IWOTH)/*所有用户可写*/#defineS_IXUGO(S_IXUSR|S_IXGRP|S_IXOTH)/*所有用户可执行*//*include/uapi/linux/stat.h*//*三者分别表示用于者权限、用户组权限、其他访问者权限*bit[0]、bit[1]、bit[2]分别表示可执行、可写、可读属性*/#defineS_IRWXU00700#defineS_IRUSR00400#defineS_IWUSR00200#defineS_IXUSR00100#defineS_IRWXG00070#defineS_IRGRP00040#defineS_IWGRP00020#defineS_IXGRP00010#defineS_IRWXO00007#defineS_IROTH00004#defineS_IWOTH00002#defineS_IXOTH00001

示例:

staticintmode=1;staticchar*p=NULL;module_param(mode,int,S_IRUGO);/*int型*/module_param(p,charp,S_IRUGO);/*指针*/

2)数组类型

module_param_array(name,type,nump,perm)

示例:

staticintarray[3]={0};staticintarray_size;module_param_array(array,int,&array_size,S_IRUGO);

3)字符串类型

module_param_string(name,string,len,perm)

示例:

staticcharstring[6]={0};module_param_string(usestr,string,sizeof(string),S_IRUGO);

4)参数回调类型

module_param_cb(name,ops,arg,perm)

这个宏用于在参数(参数)发生变化时注册回调。例如,我使用 module_param 创建了一个参数debug,一旦我加载带有 debug=0 的simple模块,它将创建一个 sysfs 条目。并且我们想在不重新加载模块的情况下打开调试消息,我们可以使用该文件:

[root@localhosttmp23]#ls-l/sys/module/simple/parameters/debug-rw-r--r--1rootroot4096May2314:46/sys/module/simple/parameters/debug

当 sysfs 的值改变时(你可以使用echo 1 > /sys/module//parameters/debug改变),我们可以通过回调得到通知。如果您想在值发生变化时收到通知,我们需要将我们的处理函数注册到它的文件操作结构中,即下面数据结构:

structkernel_param_ops{int(*set)(constchar*val,conststructkernel_param*kp);int(*get)(char*buffer,conststructkernel_param*kp);void(*free)(void*arg);}

示例:

/*----------------------Module_param_cb()--------------------------------*/staticintdebug=0;intcustom_callback_function(constchar*val,conststructkernel_param*kp){intres=param_set_int(val,kp);//Useforwritevariableif(res==0){printk(KERN_INFO"Callbackfunctioncalled...\n");printk(KERN_INFO"Newvalueofdebug=%d\n",debug);return0;}return-1;}conststructkernel_param_opsmy_param_ops={.set=&custom_callback_function,//Useoursetter....get=¶m_get_int,//..andstandardgetter};module_param_cb(debug,&my_param_ops,&debug,S_IRUGO|S_IWUSR);

执行echo 1 > /sys/module/simple/parameters/debug后,您可以看到调试变量发生了变化:

[root@localhost~]#dmesg[891496.255781]hello...debug=0[891555.726272]Callbackfunctioncalled...[891555.726277]Newvalueofdebug=1

5)参数描述

用户向内核模块传递参数时,参数较多的情况下,开发工程师不易全部记住;因此,一般都会增加准确、清晰的参数描述信息,描述不同参数代表的含义,用户调用时首先查询驱动模块的参数描叙信息,进而有目的地传入具体参数。参数描述信息通过MODULE_PARM_DESC宏实现,该宏位于“/include/linux/moduleparam.h”中定义

#defineMODULE_PARM_DESC(_parm,desc)\__MODULE_INFO(parm,_parm,#_parm":"desc)

示例:

staticintmode=0;module_param(mode,int,S_IRUGO);MODULE_PARM_DESC(mode,"0:mode0;1:mode1;2:mode2");

3、内核模块传参实例

编写一个基本的Linux内核模块程序,实现命令行往内核模块传递参数的功能,加载内核模块时传入指定参数。

内核模块源码如下:

#include#include#include#include/*传递整型类型数据*/staticintirq=10;module_param(irq,int,0660);MODULE_PARM_DESC(irq,"Interruptrange:1-32");staticintdebug=0;module_param(debug,int,0660);MODULE_PARM_DESC(debug,"0:nondebugmode;1:debugmode");/*传递指针类型数据*/staticchar*devname="simpdev";module_param(devname,charp,0660);MODULE_PARM_DESC(devname,"devicename");/*传递数组类型数据module_param_array(数组名,元素类型,元素个数(取地址),权限);*/staticintarray[3];staticintcount;module_param_array(array,int,&count,0660);MODULE_PARM_DESC(array,"setarrayvalue");/*传递字符串:module_param_string(传递参数时的字符串名称,字符串名称,字符串大小,权限);*/staticcharstring[20]={0};module_param_string(mystr,string,sizeof(string),0660);MODULE_PARM_DESC(mystr,"stringvariableofdemonstration");staticintsimple_init(void){inti=0;printk(KERN_WARNING"hello...irq=%dname=%sdebug=%d\n",irq,devname,debug);for(i=0;i<3;i++){printk("array[%d]:%d\n",i,array[i]);}printk("count=%d\n",count);printk("string=%s\n",string);return0;}staticvoidsimple_cleanup(void){printk(KERN_WARNING"bye...irq=%dname=%sdebug=%d,count=%d,string=%s\n",irq,devname,debug,count,string);}module_init(simple_init);module_exit(simple_cleanup);MODULE_LICENSE("GPL");

Makefile文件:

ifneq($(KERNELRELEASE),)$(info"2nd")obj-m:=simple.oelseKDIR:=/lib/modules/$(shelluname-r)/buildPWD:=$(shellpwd)all:$(info"1st")make-C$(KDIR)M=$(PWD)modulesclean:rm-f*.ko*.o*.mod.o*.symvers*.cmd*.mod.c*.order*.modendif

命令行执行make编译程序,如下:

[root@localhosttmp22]#make"1st"make-C/lib/modules/4.18.0-394.el8.x86_64/buildM=/tmp/tmp22modulesmake[1]:Enteringdirectory"/usr/src/kernels/4.18.0-394.el8.x86_64""2nd"CC[M]/tmp/tmp22/simple.oBuildingmodules,stage2."2nd"MODPOST1modulesCC/tmp/tmp22/simple.mod.oLD[M]/tmp/tmp22/simple.komake[1]:Leavingdirectory"/usr/src/kernels/4.18.0-394.el8.x86_64"

查看驱动模块信息,执行"modinfo simple.ko"查看驱动模块信息,可看到我们在驱动程序中添加的参数,以及参数描述信息。

[root@localhosttmp22]#modinfosimple.kofilename:/tmp/tmp22/simple.kolicense:GPLrhelversion:8.7srcversion:41AA4F4D38C05858A72127Ddepends:name:simplevermagic:4.18.0-394.el8.x86_64SMPmod_unloadmodversionsparm:irq:Interruptrange:1-32(int)parm:debug:0:nondebugmode;1:debugmode(int)parm:devname:devicename(charp)parm:array:setarrayvalue(arrayofint)parm:mystr:stringvariableofdemonstration(string)

加载内核模块,同时指定传递的参数;使用dmesg查看程序打印结果如下:

[root@localhosttmp22]#insmod./simple.koirq=22devname=mydevdebug=1array=0x1000,0x2000,0x3000mystr="kernel-module"[root@localhosttmp22]#[root@localhosttmp22]#lsmod|grepsimplesimple163840[root@localhost~]#dmesg[792494.652624]hello...irq=22name=mydevdebug=1[792494.652631]array[0]:4096[792494.652632]array[1]:8192[792494.652633]array[2]:12288[792494.652634]count=3[792494.652635]string=kernel-module#卸载simple内核模块[root@localhosttmp22]#rmmodsimple#查看卸载simple内核模块后,dmesg打印信息[root@localhost~]#dmesg[792494.652624]hello...irq=22name=mydevdebug=1[792494.652631]array[0]:4096[792494.652632]array[1]:8192[792494.652633]array[2]:12288[792494.652634]count=3[792494.652635]string=kernel-module[792615.350968]bye...irq=22name=mydevdebug=1,count=3,string=kernel-module

三、内核模块参数与sysfs文件系统

1、查看sysfs文件系统下内核参数

在带有参数的内核模块安装成功后,/sys/目录下会生成加载内核模块以后的parameters文件夹,内含以变量名字命名的文件,文件内容则为通过命令行传递的参数的值。

[root@localhosttmp22]#ls-l/sys/module/simple/parameters/total0-rw-rw----1rootroot4096May2213:22array-rw-rw----1rootroot4096May2213:22debug-rw-rw----1rootroot4096May2213:22devname-rw-rw----1rootroot4096May2213:22irq-rw-rw----1rootroot4096May2213:22mystr[root@localhosttmp22]#[root@localhosttmp22]#cat/sys/module/simple/parameters/array4096,8192,12288[root@localhosttmp22]#[root@localhosttmp22]#cat/sys/module/simple/parameters/debug1[root@localhosttmp22]#cat/sys/module/simple/parameters/devnamemydev[root@localhosttmp22]#cat/sys/module/simple/parameters/irq22[root@localhosttmp22]#cat/sys/module/simple/parameters/mystrkernel-module

当内核模块卸载以后,则在/sys目录下的以模块名命名的文件夹则会被清除掉。

[root@localhosttmp22]#ls-l/sys/module/|grepsimple[root@localhosttmp22]#

2、手动修改sysfs文件,回调通知内核

编写一个基本的Linux内核模块程序,实现修改sysfs文件后,通知内核模块的功能。

内核模块源码如下:

#include#include#include#includestaticintdebug=0;MODULE_PARM_DESC(debug,"0:nondebugmode;1:debugmode");/*----------------------Module_param_cb()--------------------------------*/intcustom_callback_function(constchar*val,conststructkernel_param*kp){intres=param_set_int(val,kp);//Useforwritevariableif(res==0){printk(KERN_INFO"Callbackfunctioncalled...\n");printk(KERN_INFO"Newvalueofdebug=%d\n",debug);return0;}return-1;}conststructkernel_param_opsmy_param_ops={.set=&custom_callback_function,//Useoursetter....get=¶m_get_int,//..andstandardgetter};module_param_cb(debug,&my_param_ops,&debug,S_IRUGO|S_IWUSR);staticintsimple_init(void){printk(KERN_WARNING"hello...debug=%d\n",debug);return0;}staticvoidsimple_cleanup(void){printk(KERN_WARNING"bye...debug=%d\n",debug);}module_init(simple_init);module_exit(simple_cleanup);MODULE_LICENSE("GPL");

实际的参数值保存在debug中,set函数将用户输入作为字符串并将其转换为int,并使用内核提供的param_set_int函数进行设置。要返回参数值,我们使用内核提供的param_get_int函数。

Makefile文件:

ifneq($(KERNELRELEASE),)$(info"2nd")obj-m:=simple.oelseKDIR:=/lib/modules/$(shelluname-r)/buildPWD:=$(shellpwd)all:$(info"1st")make-C$(KDIR)M=$(PWD)modulesclean:rm-f*.ko*.o*.mod.o*.symvers*.cmd*.mod.c*.order*.modendif

编译源文件,并加载相应模块,结果如下:

[root@localhosttmp23]#make"1st"make-C/lib/modules/4.18.0-394.el8.x86_64/buildM=/tmp/tmp23modulesmake[1]:Enteringdirectory"/usr/src/kernels/4.18.0-394.el8.x86_64""2nd"CC[M]/tmp/tmp23/simple.oBuildingmodules,stage2."2nd"MODPOST1modulesCC/tmp/tmp23/simple.mod.oLD[M]/tmp/tmp23/simple.komake[1]:Leavingdirectory"/usr/src/kernels/4.18.0-394.el8.x86_64"[root@localhosttmp23]#lsMakefilemodules.orderModule.symverssimple.csimple.kosimple.mod.csimple.mod.osimple.o[root@localhosttmp23]#[root@localhosttmp23]#[root@localhosttmp23]#lsmod|grepsimplesimple163840[root@localhosttmp23]#rmmodsimple[root@localhosttmp23]#[root@localhosttmp23]#lsMakefilemodules.orderModule.symverssimple.csimple.kosimple.mod.csimple.mod.osimple.o[root@localhosttmp23]#[root@localhosttmp23]#insmod./simple.ko[root@localhosttmp23]#lsmod|grepsimplesimple163840[root@localhosttmp23]#[root@localhosttmp23]#ls-l/sys/module/simple/parameters/debug-rw-r--r--1rootroot4096May2314:46/sys/module/simple/parameters/debug[root@localhosttmp23]#cat/sys/module/simple/parameters/debug0#此时,查看demsg信息,如下:[root@localhost~]#dmesg[891496.255781]hello...debug=0#修改sysfs条目内容[root@localhosttmp23]#echo1>/sys/module/simple/parameters/debug[root@localhosttmp23]#[root@localhosttmp23]#cat/sys/module/simple/parameters/debug1#此时,查看dmesg信息,可以看到debug变量的值已被修改成1[root@localhost~]#dmesg[891496.255781]hello...debug=0[891555.726272]Callbackfunctioncalled...[891555.726277]Newvalueofdebug=1

这是一口君的新书,感谢大家支持!

精彩文章合集

文章推荐

关键词:

最新新闻
回顶部