【内核驱动】内核模块传参与符号共享

00. 目录

01. 概述

内核模块作为一个可拓展的动态模块,为Linux内核提供了灵活性,但是有时我们需要根据不同的应用场景给内核传递不同的参数, 例如在程序中开启调试模式、设置详细输出模式以及制定与具体模块相关的选项,都可以通过参数的形式来改变模块的行为。

在这里插入图片描述

02. module_param

Linux内核提供一个宏来实现模块的参数传递

module_param函数 (内核源码/include/linux/moduleparam.h)

/**
 * module_param - typesafe helper for a module/cmdline parameter
 * @value: the variable to alter, and exposed parameter name.
 * @type: the type of the parameter
 * @perm: visibility in sysfs.
 *
 * @value becomes the module parameter, or (prefixed by KBUILD_MODNAME and a
 * ".") the kernel commandline parameter.  Note that - is changed to _, so
 * the user can use "foo-bar=1" even for variable "foo_bar".
 *
 * @perm is 0 if the the variable is not to appear in sysfs, or 0444
 * for world-readable, 0644 for root-writable, etc.  Note that if it
 * is writable, you may need to use kernel_param_lock() around
 * accesses (esp. charp, which can be kfreed when it changes).
 *
 * The @type is simply pasted to refer to a param_ops_##type and a
 * param_check_##type: for convenience many standard types are provided but
 * you can create your own by defining those variables.
 *
 * Standard types are:
 *  byte, short, ushort, int, uint, long, ulong
 *  charp: a character pointer
 *  bool: a bool, values 0/1, y/n, Y/N.
 *  invbool: the above, only sense-reversed (N = true).
 */
#define module_param(name, type, perm)              \
    module_param_named(name, name, type, perm)


/**
 * module_param_array - a parameter which is an array of some type
 * @name: the name of the array variable
 * @type: the type, as per module_param()
 * @nump: optional pointer filled in with the number written
 * @perm: visibility in sysfs
 *
 * Input and output are as comma-separated values.  Commas inside values
 * don't work properly (eg. an array of charp).
 *
 * ARRAY_SIZE(@name) is used to determine the number of elements in the
 * array, so the definition must be visible.
 */
#define module_param_array(name, type, nump, perm)      \
    module_param_array_named(name, name, type, nump, perm)

以上代码中的module_param函数需要传入三个参数:

  • name: 我们定义的变量名;
  • 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。
  • perm: 表示的是该文件的权限,具体参数值见下表。
用户组 标志位 解释
当前用户 S_IRUSR 用户拥有读权限
S_IWUSR 用户拥有写权限
当前用户组 S_IRGRP 当前用户组的其他用户拥有读权限
S_IWUSR 当前用户组的其他用户拥有写权限
其他用户 S_IROTH 其他用户拥有读权限
S_IWOTH 其他用户拥有写权限

上述文件权限唯独没有关于可执行权限的设置,请注意, 该文件不允许它具有可执行权限。如果强行给该参数赋予表示可执行权限的参数值S_IXUGO, 那么最终生成的内核模块在加载时会提示错误。

程序示例

static int itype = 0;
module_param(itype, int, 0);

static bool btype = 0;
module_param(btype, bool, 0644);

static char ctype = 0;
module_param(ctype, byte, 0);

static char  *stype = 0;
module_param(stype, charp, 0644);

static int __init param_init(void)
{
    
    
   printk(KERN_ALERT "param init!\n");
   printk(KERN_ALERT "itype=%d\n",itype);
   printk(KERN_ALERT "btype=%d\n",btype);
   printk(KERN_ALERT "ctype=%d\n",ctype);
   printk(KERN_ALERT "stype=%s\n",stype);
   return 0;
}
  • 第1-11行:定义了四个常见变量然后使用module_param宏来声明这四个参数
  • 第13-21行:并在param_init中输出上面声明的四个参数。

03. 符号共享代码

在前面已经详细的分析了关于导出符号的内核源码,符号指的就是在内核模块中导出函数和变量, 在加载模块时被记录在公共内核符号表中,以供其他模块调用。 这个机制,允许我们使用分层的思想解决一些复杂的模块设计。我们在编写一个驱动的时候, 可以把驱动按照功能分成几个内核模块,借助符号共享去实现模块与模块之间的接口调用,变量共享。

/* For every exported symbol, place a struct in the __ksymtab section */
#define __EXPORT_SYMBOL(sym, sec)               \
    extern typeof(sym) sym;                 \
    __CRC_SYMBOL(sym, sec)                  \
    static const char __kstrtab_##sym[]         \
    __attribute__((section("__ksymtab_strings"), aligned(1))) \
    = VMLINUX_SYMBOL_STR(sym);              \
    extern const struct kernel_symbol __ksymtab_##sym;  \
    __visible const struct kernel_symbol __ksymtab_##sym    \
    __used                          \
    __attribute__((section("___ksymtab" sec "+" #sym), unused)) \
    = {
      
       (unsigned long)&sym, __kstrtab_##sym }

#define EXPORT_SYMBOL(sym)                  \
    __EXPORT_SYMBOL(sym, "")

EXPORT_SYMBOL宏用于向内核导出符号,这样的话,其他模块也可以使用我们导出的符号了。 下面通过一段代码,介绍如何使用某个模块导出符号。

扫描二维码关注公众号,回复: 16673408 查看本文章

程序示例

static int itype = 0;
module_param(itype, int, 0);

EXPORT_SYMBOL(itype);

int my_add(int a, int b)
{
    
    
   return a + b;
}

EXPORT_SYMBOL(my_add);

int my_sub(int a, int b)
{
    
    
   return a - b;
}

EXPORT_SYMBOL(my_sub);
  • 第2-3行:定义了参数itype,并通过EXPORT_SYMBOL宏导出
  • 第7-12行:和my_add,并通过EXPORT_SYMBOL宏导出
  • 第14-21行:my_sub函数,并通过EXPORT_SYMBOL宏导出

04. 程序示例

add.h

#ifndef __ADD_H__
#define __ADD_H__

extern int itype;

int my_add(int a, int b);
int my_sub(int a, int b);

#endif

insmod test.ko itype=123 btype=1 ctype=200 stype=abc

add.c

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

#include "add.h"

static int __init cal_init(void)
{
    
    
    printk(KERN_ALERT "cal init!\n");
    printk(KERN_ALERT "itype+1 = %d, itype-1 = %d\n", my_add(itype, 1), my_sub(itype, 1));    

    return 0;
}

static void __exit cal_exit(void)
{
    
    
    printk(KERN_ALERT "cal exit!\n");
}


module_init(cal_init);
module_exit(cal_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("uplooking");
MODULE_DESCRIPTION("cal module");
MODULE_ALIAS("cal_module");

test.c

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

static int itype = 0;
module_param(itype, int, 0);

static bool btype = 0;
module_param(btype, bool, 0700);

static char ctype = 0;
module_param(ctype, byte, 0);

static char  *stype = 0;
module_param(stype, charp, 0644);

static int __init param_init(void)
{
    
    
   printk(KERN_ALERT "param init!\n");
   printk(KERN_ALERT "itype=%d\n",itype);
   printk(KERN_ALERT "btype=%d\n",btype);
   printk(KERN_ALERT "ctype=%d\n",ctype);
   printk(KERN_ALERT "stype=%s\n",stype);
   return 0;
}

static void __exit param_exit(void)
{
    
    
   printk(KERN_ALERT "module exit!\n");
}

EXPORT_SYMBOL(itype);

int my_add(int a, int b)
{
    
    
   return a+b;
}

EXPORT_SYMBOL(my_add);

int my_sub(int a, int b)
{
    
    
   return a-b;
}

EXPORT_SYMBOL(my_sub);

module_init(param_init);
module_exit(param_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("uplooking");
MODULE_DESCRIPTION("module_param");
MODULE_ALIAS("module_param");

Makefile

CROSS_COMPILE=aarch64-linux-gnu-gcc

obj-m := test.o add.o

all:
	$(MAKE) -C $(KERNEL_DIR) M=`pwd` modules

.PHONE:clean
clean:
	$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean

05. 未完待续

06. 附录

猜你喜欢

转载自blog.csdn.net/dengjin20104042056/article/details/132884351
今日推荐