概述
在实际应用中,模块和程序往往需要适应各种系统环境,针对不同环境,参数往往也会有所不同,为了提高灵活性,我们可以将参数从外部传入,让其适应更多系统和环境。
应用程序
众所周知,应用程序的入口函数为main函数,而main函数原型为:int main(int argc, char* argv[])。应用程序参数传入后,由argc和argv变量记录。其中argc存储参数个数,argv存储参数集合。
注意:应用程序将所有参数均存储为字符串类型,存放在argv参数中。具体见以下示例。
/** * @Filename : app_para.c * @Revision : $Revision: 1.00 $ * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅) * @Description : 应用程序传参示例 **/ #include <stdio.h> #include <stdlib.h> int main(int argc, char* argv[]) { int a; int b; if (argc != 3) { printf("Usage: %s <a> <b>\n", argv[0]); return -1; } /* 将字符串转整型: 如 "20" -> 20 */ a = strtoul(argv[1], NULL, 0); b = strtoul(argv[2], NULL, 0); printf("%d + %d = %d\n", a, b, a+b); return 0; }
测试:在终端通过命令编译程序,然后执行程序并传入参数。
feng:drv_para$ gcc -o app app_para.c feng:drv_para$ ./app 6 12 6 + 12 = 18 feng:drv_para$
结论:示例接收来自命令行传入的参数(6,12)后,将其转换为整型,执行加法处理后输出结果(18)。
feng:drv_para$ ./app 6 12 执行后: argc = 3 argv[0] = ./app argv[1] = 6 argv[2] = 12
内核模块
与应用程序不同,内核模块传参主要通过宏函数module_param和module_param_array实现,在头文件linux/modulepara.h中定义。
/** * @传递参数 * @name:参数名字 type:参数类型 perm:权限 **/ #define module_param(name, type, perm) \ module_param_named(name, name, type, perm) /** * @传递数组参数 * @name:数组名字 type:数组类型 nump:保存数组个数 perm:权限 **/ #define module_param_array(name, type, nump, perm) \ module_param_array_named(name, name, type, nump, perm)
内核模块支持的参数类型:
bool/invbool /* 布尔值,invbool表示反转bool值,即true变false) */ charp /* 字符串指针值 */ int/uint /* 整型/无符号整型 */ short/ushort /* 短整型/无符号短整型 */ long/ulong /* 长整型/无法好长整型 */
参数权限:在linux/stat.h头文件中定义,用户大多数情况下指定为只读(S_IRUGO)即可。
/** * @读写权限,位置:linux/stat.h **/ #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 #define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO) /* 读/写/执行 */ #define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO) #define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) /* 只读,绝大多数选择 */ #define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) /* 只写 */ #define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) /* 只能执行 */
在传递数组参数时,nump用于存储实际传入数组元素个数。
示例
★包含源文件drv_para.c和源文件Makefile(均已验证通过)。
drv_para.c
/** * @Filename : drv_para.c * @Revision : $Revision: 1.00 $ * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅) * @Description : 驱动模块传参示例 **/ #include <linux/init.h> #include <linux/module.h> /* 定义全局变量 */ static int val; static char *ps; static int buf[5] = {0, 1, 2, 3, 4}; static int num = 1; /* 传參声明 */ module_param(val, int, S_IRUGO); module_param(ps, charp, S_IRUGO); module_param_array(buf , int , &num , S_IRUGO); /* 驱动加载时执行,调用insmod或者modprobe加载驱动 */ static int __init drv_para_init(void) { int i; printk("hello : drv_para_init val[%d] ps[%s] buf[ ", val, ps); for (i=0; i<num; i++) printk("%d ", buf[i]); printk("]\n"); return 0; } /* 驱动卸载时执行,调用rmsmod或者modprobe -r卸载驱动 */ static void __exit drv_para_exit(void) { printk("bye : drv_para_exit\n"); } module_init(drv_para_init); /* 指定入口函数 */ module_exit(drv_para_exit); /* 指定出口函数 */ MODULE_LICENSE("GPL"); /* 模块的许可证声明 */ /* 调用modinfo xx(模块名)查看 */ MODULE_AUTHOR("feng"); /* 模块的作者 */ MODULE_VERSION ("1.00"); /* 模块版本号 */ /* MODULE_DESCRIPTION("xxxxx"); 模块描述 */ /* MODULE_ALIAS("xxx"); 模块别名 */
Makefile
#根文件所在目录 ROOTFS_DIR = /home/feng/atomic/rootfs #交叉编译工具链 CROSS_COMPILE = arm-linux-gnueabihf- CC = $(CROSS_COMPILE)gcc #驱动目录路径 DRV_DIR = $(ROOTFS_DIR)/home/drv DRV_DIR_LIB = $(ROOTFS_DIR)/lib/modules/4.1.15 #KERNELRELEASE由内核makefile赋值 ifeq ($(KERNELRELEASE), ) #内核路径 KERNEL_DIR =/home/feng/atomic/resource/linux-imx-rel_imx_4.1.15_2.1.0_ga #当前文件路径 CURR_DIR = $(shell pwd) all: #编译模块 make -C $(KERNEL_DIR) M=$(CURR_DIR) modules clean: #清除模块文件 make -C $(KERNEL_DIR) M=$(CURR_DIR) clean install: #拷贝模块文件 cp -raf *.ko $(DRV_DIR_LIB) else #指定编译什么文件 obj-m += drv_para.o endif
结论
1、进入目录,执行make命令编译模块;然后执行make install命令,拷贝模块到目标机指定目录。
feng:drv_para$ make #编译模块 make -C /home/feng/atomic/resource/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/mnt/hgfs/Share/linux/atomic/driver/drv_para modules make[1]: 进入目录“/home/feng/atomic/resource/linux-imx-rel_imx_4.1.15_2.1.0_ga” CC [M] /mnt/hgfs/Share/linux/atomic/driver/drv_para/drv_para.o Building modules, stage 2. MODPOST 1 modules CC /mnt/hgfs/Share/linux/atomic/driver/drv_para/drv_para.mod.o LD [M] /mnt/hgfs/Share/linux/atomic/driver/drv_para/drv_para.ko make[1]: 离开目录“/home/feng/atomic/resource/linux-imx-rel_imx_4.1.15_2.1.0_ga” feng:drv_para$ make install #拷贝模块文件 cp -raf *.ko /home/feng/atomic/rootfs/lib/modules/4.1.15 feng:drv_para$
2、在目标机上执行modprobe命令加载模块,并传入参数。
注意:若未传入参数,程序使用代码里定义的初始值。
/ # modprobe drv_para.ko val=7 ps=seven buf=3,2,5,6,7 hello : drv_para_init val[7] ps[seven] buf[ 3 2 5 6 7 ] / #
3、在目标机上执行modprobe -r命令卸载模块。
/ # modprobe -r drv_para.ko bye : drv_para_exit / #
4、综上、加载模块的时候,用户通过命令行传入参数val、ps、buf,模块接收到参数后,输出对应的值,从而验证外部传参是否正确。
往期 · 推荐
帮你自动化办公的python-自动提取pdf指定页(文件处理篇)
帮你自动化办公的python-自动提取pdf指定页(项目概述)
也没想象中那么神秘的数据结构-一种通用化的双向链表设计(底层源码)
关注
更多精彩内容,请关注微信公众号:不只会拍照的程序猿,本人致力分享linux、设计模式、C语言、嵌入式、编程相关知识,也会抽空分享些摄影相关内容,同样也分享大量摄影、编程相关视频和源码,另外你若想要本文章源码请关注公众号:不只会拍照的程序猿,后台回复:linux驱动源码。