Linux应用程序、shell脚本程序、驱动程序参数传递的方法

  在嵌入式linux应用开发中,可以给main()函数传递参数,这样应用程序就能知道最初的控制参数是什么,当然也可以选择不向应用程序传递参数。也可以在执行 Shell 脚本时,向脚本传递参数。在驱动开发中,会使用到insmod命令来加载一个驱动模块,这时候我们也可以使用insmod命令向驱动模块传递参数。

一、向Linux应用程序传参

1.1、传参方法

  在 Linux 应用程序中,main 函数作为应用程序的入口函数,main 函数的形参一般会有两种写法,如果执行应用程序无需传参,则可以写成如下形式:

int main(void)
{
    
    
 /* 代码 */
}

  如果在执行应用程序的时候需要向应用程序传递参数,则写法如下:

int main(int argc, char **argv)
{
    
    
 /* 代码 */
}

  argc 形参表示传入参数的个数,包括应用程序自身路径和程序名,譬如运行当前目录下的 hello 可执行文件,并且传入参数,如下所示:

./hello 123

  那么此时参数个数为 2,并且这些参数都是作为字符串的形式传递给 main 函数:
  argv[0]等于"./hello"
  argv[1]等于"123"

1.2、代码示例

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
    
    
	int fd, retvalue;
	char *filename;
	unsigned char databuf[1];
	if(argc != 3)
	{
    
    
		printf("Error Usage!\r\n");
		return -1;
	}
	filename = argv[1];
	fd = open(filename, O_RDWR);
	if(fd < 0)
	{
    
    
		printf("file %s open failed!\r\n", argv[1]);
		return -1;
	}
	databuf[0] = atoi(argv[2]);	
	/* 向文件写入数据 */
	retvalue = write(fd, databuf, sizeof(databuf));
	if(retvalue < 0)
	{
    
    
		printf("LED Control Failed!\r\n");
		close(fd);
		return -1;
	}
	retvalue = close(fd); /* 关闭文件 */
	if(retvalue < 0)
	{
    
    
		printf("file %s close failed!\r\n", argv[1]);
		return -1;
	}
	return 0;
}

  编译后,可以通过以下命令来运行该程序

./test /dev/led 1 //打开 LED 灯

二、向shell脚本程序传参

2.1、传参介绍

  我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
  以下实例我们向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名:

#!/bin/bash
echo "Shell 传递参数!";
echo "文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";

  为脚本设置可执行权限,并执行脚本,输出结果如下所示:
在这里插入图片描述

2.2、特殊字符

   Shell 脚本还有几个特殊字符用来处理参数

$#	传递到脚本的参数个数
$*	以一个单字符串显示所有向脚本传递的参数。
     如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$$	脚本运行的当前进程ID号
$!	后台运行的最后一个进程的ID号
$@	与$*相同,但是使用时加引号,并在引号中返回每个参数。
    如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$-	显示Shell使用的当前选项,与set命令功能相同。
$?	显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

  使用示例:

#!/bin/bash
echo "Shell 传递参数实例!";
echo "第一个参数为:$1";
echo "参数个数为:$#";
echo "传递的参数作为一个字符串显示:$*";

在这里插入图片描述

2.3、$* 与 $@ 区别

  相同点:都是引用所有参数。
  不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 " * " 等价于 “1 2 3”(传递了一个参数),而 “@” 等价于 “1” “2” “3”(传递了三个参数)。

#!/bin/bash
echo "-- \$* 演示 ---"
for i in "$*"; do
    echo $i
done

echo "-- \$@ 演示 ---"
for i in "$@"; do
    echo $i
done

  执行脚本,输出结果如下所示:
在这里插入图片描述

三、向驱动模块程序传参

3.1、参数传递方式

  参数传递分为两种
  内置模块参数传递:即将模块编译构建进内核镜像。
  外置模块参数传递:使用insmod等命令装在的内核模块。
  对于内置模块参数传递,一般在bootloader中可向内置的模块传递参数,例如可以在bootargs中设置模块名.参数名=值的形式给该内置的模块传递参数;对于外置模块,在装载内核模块时,我们可以向模块传递参数,形式为:

insmode(或 modprobe)模块名 参数名=参数值

  如果不传递参数,参数将使用模块内定义的缺省值。

3.2、参数传递方法

3.2.1、变量权限

  向驱动模块传递参数,必须事先在驱动源码中声明某一个变量可作为模块参数传递,并且指定变量的权限,常见的权限参数如下:
在这里插入图片描述
  上述宏定义中,S_I是公共的写法,R = read,W = write ,X = execute , USR = user,GPR = group。
  当然,也可以这样看:可以将数字最后三位转化为二进制:xxx xxx xxx,高位往低位依次看,第一位为 1 表示文件所有者可读,第二位为 1 表示文件所有者可写,第三位为 1 表示文件所有者可执行;接下来三位表示文件所有者同组成员的权限;再下来三位为不同组用户权限。

使用'|'(或操作),可以一次设置多个权限。

3.2.2、传递普通的参数

  传递普通的参数(例如 bool、char、int),使用下列宏定义:

module_param(name,type,perm)

  name:要传递进去参数的名称。
  type:要传递进去参数的类型。
  perm:要传递进去参数的读写权限。
  module_param()宏在/sys/module下创建子目录。上述代码,将在/sys/module/中可查看对应的参数:

3.2.3、传递数组

  传递数组参数使用下列的宏定义:

module_param_array(name,type,nump,perm)

  name:要传递进去参数的名称。
  type:要传递进去参数的类型。
  nump:实际传入进去参数的个数。
  perm:要传递进去参数的读写权限。

3.2.4、参数改变时回调

  可以向/sys/module/下的参数名称写入参数,这意味将改变参数的值,如果使用module_param()和module_param_array(),则无法获知参数的值是否改变,如果我们想要让系统获知参数的改变,并根据参数改变进一步执行处理,则需要使用module_param_cb()注册参数值改变时的处理函数。在内核中,module_param_cb()宏用于注册回调,每当参数(形参)发生变化时,将调用此回调函数:

module_param_cb(name, ops, arg, perm) 

  name:要传递进去参数的名称。
  ops:该参数的set和get操作。
  arg:传递给ops中回到函数的参数。
  perm:要传递进去参数的读写权限。

3.3、代码示例

  编写内核代码如下:

#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
 
int value, arr_data[4];
char *name;
int callback_date = 0;

//声明模块参数
module_param(value, int, S_IRUSR|S_IWUSR);                      //integer 类型
module_param(name, charp, S_IRUSR|S_IWUSR);                     //String  类型
module_param_array(arr_data, int, NULL, S_IRUSR|S_IWUSR);      //整型数组
 
/*----------------------Module_param_cb()--------------------------------*/
int notify_param(const char *val, const struct kernel_param *kp)
{
    
    
    int res = param_set_int(val, kp); // Use helper for write variable
    
    if(res == 0) 
	{
    
    
        printk(KERN_INFO "Call back function called...\n");
        printk(KERN_INFO "New value of callback_date = %d\n", callback_date);
        return 0;
    }
    return -1;
}
 
const struct kernel_param_ops my_param_ops = 
{
    
    
    .set = notify_param, 
    .get = param_get_int, 
};
 
module_param_cb(callback_date, &my_param_ops, &callback_date, S_IRUGO|S_IWUSR|S_IWGRP );

static int __init test_init(void)
{
    
    
    int i;
    //打印出模块参数
    printk(KERN_INFO "Value = %d  \n", value);
    printk(KERN_INFO "callback_date = %d  \n", callback_date);
    printk(KERN_INFO "Name = %s \n", name);
    for (i = 0; i < (sizeof arr_data / sizeof (int)); i++) 
	{
    
    
        printk(KERN_INFO "arr_data[%d] = %d\n", i, arr_data[i]);
    }
    printk(KERN_INFO "Kernel Module Inserted Successfully...\n");
    return 0;
}


static void __exit test_exit(void)
{
    
    
    printk(KERN_INFO "Kernel Module Removed Successfully...\n");
}
 
module_init(test_init);
module_exit(test_exit);
 
MODULE_LICENSE("GPL");

  测试代码运行在ubuntu的虚拟机里面,参照https://blog.csdn.net/xxxx123041/article/details/131560875编写相对应的makefile文件如下:

KERNELDIR :=/usr/src/linux-headers-5.15.0-79-generic
CURRENT_PATH := $(shell pwd)
obj-m := mytest.o
build: kernel_modules
kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean


3.4、加载驱动程序测试

  执行make命令编译,编译结果如下图所示:
在这里插入图片描述
  使用insmod加载内核驱动模块并传递参数
在这里插入图片描述
  使用dmesg命令查看内核的打印,可以看到参数已经传递了进去。
在这里插入图片描述
  在加载内核驱动前的/sys/module目录下并没有mytest驱动模块,在加载驱动之后sys/module目录下出现了mytest驱动模块
在这里插入图片描述
在这里插入图片描述
  向该模块下的parametes目录下的callback_data参数传递数据,如下图所示
在这里插入图片描述
  再次使用dmesg命令查看内核打印,可以看到,回调函数已经执行:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xxxx123041/article/details/132305166
今日推荐