内核基础架构---模块参数

简介:    我们在编写驱动的时,可能需要变成成模块(*.ko)来加载,比如常用的wifi驱动,在模块加载的时候,我们还希望能够动态的传递一些参数,这时候就需要Module_param系列宏,参考内核2.
   我们在编写驱动的时,可能需要变成成模块(*.ko)来加载,比如常用的wifi驱动,在模块加载的时候,我们还希望能够动态的传递一些参数,这时候就需要 Module_param系列宏,参考内核2.6.32.60  至于例子搜一下代码里很多尤其drivers目录下^^. 
 我们先看一个shell脚本,安装模块:

#########

   insmod net_dev.ko param_queue_id=${QUEUE_ID} param_board=$BOARD \

                    param_tcont_id=$TCONT_ID param_us_gem_flow_id=$US_GEMFLOW_ID
真正代码层:

static int param_queue_id =3;

module_param(param_queue_id, int, 0);

 

int map_ds_traffic_to_cpu = 0;

module_param(map_ds_traffic_to_cpu, int, 0);
static char param_board[20]="";
module_param_string(param_board, param_board, sizeof(param_board), 0);

//

参数用 module_param 宏定义来声明, 它定义在 moduleparam.h.

module_param(name,type,perm);

module_param 使用了 3 个参数: 变量名, 它的类型, 以及一个权限掩码用来做一个辅助的 sysfs 入口(查看/sys/module/xXX/会发现里面有我们对应的参数,当然必须有至少读的权限,默认0,为任何人都没有权限,甚至不可见). 这个宏定义应当放在任何函数之外, 典型地是出现在源文件的前面.

数组参数, 用逗号间隔的列表提供的值, 模块加载者也支持. 声明一个数组参数, 使用:

  module_param_array(name,type,num,perm);

  这里 name 是你的数组的名称(也是参数名),

  type 是数组元素的类型,

  num 是一个整型变量,

  perm 是通常的权限值.

如果数组参数在加载时设置, num 被设置成提供的数的个数. 模块加载者拒绝比数组能放下的多的值. module_param_array() 宏的第三个参数用来记录用户insmod 时提供的给这个数组的元素个数,NULL 表示不关心用户提供的个数

如果模块参数是一个字符串时,通常使用charp类型定义这个模块参数。内核复制用户提供的字符串到内存,并且相对应的变量指向这个字符串。

例如:
static char *name;
module_param(name, charp, 0);

另一种方法是通过宏module_param_string()让内核把字符串直接复制到程序中的字符数组内。
module_param_string(name, string, len, perm);

这里,name是外部的参数名,string是内部的变量名,len是以string命名的buffer大小(可以小于buffer的大小,但是没有意义),perm表示sysfs的访问权限(或者perm是零,表示完全关闭相对应的sysfs项)。

例如:
static char species[BUF_LEN];
module_param_string(specifies, species, BUF_LEN, 0);

如果需要传递多个参数可以通过宏module_param_array()实现。 
module_param_array(name, type, nump, perm);
其中,name既是外部模块的参数名又是程序内部的变量名,type是数据类型,perm是sysfs的访问权限。指针nump指向一个整数,其值表示有多少个参数存放在数组name中。值得注意是name数组必须静态分配.
   说了这么多,来让我们看看它的本质吧.
    include/linux/moduleparam.h
   

点击(此处)折叠或打开

  1. /* This is the fundamental function for registering boot/module
  2.    parameters. perm sets the visibility in sysfs: 000 means it's
  3.    not there, read bits mean it's readable, write bits mean it's
  4.    writable. */
  5. #define __module_param_call(prefix, name, set, get, arg, isbool, perm)    \
  6.     /* Default value instead of permissions? */            \
  7.     static int __param_perm_check_##name __attribute__((unused)) =    \
  8.     BUILD_BUG_ON_ZERO((perm) 0 || (perm) > 0777 || ((perm) & 2))    \
  9.     + BUILD_BUG_ON_ZERO(sizeof(""prefix) > MAX_PARAM_PREFIX_LEN);    \
  10.     static const char __param_str_##name[] = prefix #name;        \
  11.     static struct kernel_param __moduleparam_const __param_##name    \
  12.     __used                                \
  13.     __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
  14.     = { __param_str_##name, perm, isbool ? KPARAM_ISBOOL : 0,    \
  15.      set, get, { arg } }

  16. #define module_param_call(name, set, get, arg, perm)             \
  17.     __module_param_call(MODULE_PARAM_PREFIX,             \
  18.              name, set, get, arg,             \
  19.              __same_type(*(arg), bool), perm)

  20. /* Helper functions: type is byte, short, ushort, int, uint, long,
  21.    ulong, charp, bool or invbool, or XXX if you define param_get_XXX,
  22.    param_set_XXX and param_check_XXX. */
  23. #define module_param_named(name, value, type, perm)             \
  24.     param_check_##type(name, &(value));                 \
  25.     module_param_call(name, param_set_##type, param_get_##type, &value, perm); \
  26.     __MODULE_PARM_TYPE(name, #type)

  27. #define module_param(name, type, perm)                \
  28.     module_param_named(name, name, type, perm)
仔细上上面代码里层层剥离,其实开始就是权限检测,定义static const char __param_str_##name[] = prefix #name; 然后初始化struct kernel_param(其实就是把要保存的参数信息放这里),并放到内存节区 :__param中.
那么我们看下  struct kernel_param


点击(此处)折叠或打开

  1. struct kernel_param {
  2.     const char *name;
  3.     u16 perm;
  4.     u16 flags;
  5.     param_set_fn set;
  6.     param_get_fn get;
  7.     union {
  8.         void *arg;
  9.         const struct kparam_string *str;
  10.         const struct kparam_array *arr;
  11.     };
  12. };

  13. /* Special one for strings we want to copy into */
  14. struct kparam_string {
  15.     unsigned int maxlen;
  16.     char *string;
  17. };

  18. /* Special one for arrays */
  19. struct kparam_array
  20. {
  21.     unsigned int max;
  22.     unsigned int *num;
  23.     param_set_fn set;
  24.     param_get_fn get;
  25.     unsigned int elemsize;
  26.     void *elem;
  27. };
那么这里也简单的说一下模块加载的过程. 用户空间只是直接调用了系统调用sys_init_Module.对模块或者参数并没有处理. 这个接口在kernel/module.c 中

点击(此处)折叠或打开

  1. /* This is where the real work happens */
  2. SYSCALL_DEFINE3(init_module, void __user *, umod,
  3.         unsigned long, len, const char __user *, uargs)
  4. {
  5.     struct module *mod;
  6.     int ret = 0;

  7.     /* Must have permission */
  8.     if (!capable(CAP_SYS_MODULE) || modules_disabled)
  9.         return -EPERM;

  10.     /* Only one module load at a time, please */
  11.     if (mutex_lock_interruptible(&module_mutex) != 0)
  12.         return -EINTR;

  13.     /* Do all the hard work */
  14.     mod = load_module(umod, len, uargs);
  15.     if (IS_ERR(mod)) {
  16.         mutex_unlock(&module_mutex);
  17.         return PTR_ERR(mod);
  18.     }

  19.     /* Drop lock so they can recurse */
  20.     mutex_unlock(&module_mutex);

  21.     blocking_notifier_call_chain(&module_notify_list,
  22.             MODULE_STATE_COMING, mod);

  23.     do_mod_ctors(mod);
  24.     /* Start the module */
  25.     if (mod->init != NULL)
  26.         ret = do_one_initcall(mod->init);
  27.     if (ret 0) {
  28.         /* Init routine failed: abort. Try to protect us from
  29.                    buggy refcounters. */
  30.         mod->state = MODULE_STATE_GOING;
  31.         synchronize_sched();
  32.         module_put(mod);
  33.         blocking_notifier_call_chain(&module_notify_list,
  34.                      MODULE_STATE_GOING, mod);
  35.         mutex_lock(&module_mutex);
  36.         free_module(mod);
  37.         mutex_unlock(&module_mutex);
  38.         wake_up(&module_wq);
  39.         return ret;
  40.     }
  41.     if (ret > 0) {
  42.         printk(KERN_WARNING
  43. "%s: '%s'->init suspiciously returned %d, it should follow 0/-E convention\n"
  44. "%s: loading module anyway...\n",
  45.          __func__, mod->name, ret,
  46.          __func__);
  47.         dump_stack();
  48.     }

  49.     /* Now it's a first class Wake up anyone waiting for it. */
  50.     mod->state = MODULE_STATE_LIVE;
  51.     wake_up(&module_wq);
  52.     blocking_notifier_call_chain(&module_notify_list,
  53.                  MODULE_STATE_LIVE, mod);

  54.     /* We need to finish all async code before the module init sequence is done */
  55.     async_synchronize_full();

  56.     mutex_lock(&module_mutex);
  57.     /* Drop initial reference. */
  58.     module_put(mod);
  59.     trim_init_extable(mod);
  60. #ifdef CONFIG_KALLSYMS
  61.     mod->num_symtab = mod->core_num_syms;
  62.     mod->symtab = mod->core_symtab;
  63.     mod->strtab = mod->core_strtab;
  64. #endif
  65.     module_free(mod, mod->module_init);
  66.     mod->module_init = NULL;
  67.     mod->init_size = 0;
  68.     mod->init_text_size = 0;
  69.     mutex_unlock(&module_mutex);

  70.     return 0;
  71. }
里面核心函数load_module,把模块代码和数据从用户空间copy到内核空间,等。因为我们知道ko文件其实也是elf格式文件,至于如何解读和执行elf文件,大家可以阅读elf文件格式和文档,这里不过多解释.相当于用户空间执行解析elf,放到了内核来做,当然也是有区别的^^.
查看模块信息:

modinfo [-adhpv][模块文件]

 功能:显示kernel模块的信息。

 参数:
       -a或--author  显示模块开发人员。 

  -d或--description  显示模块的说明。 
     -h或--help  显示modinfo的参数使用方法。 
     -p或--parameters  显示模块所支持的参数。 
     -V或--version  显示版本信息。


既然上面说到了模块参数,这里顺便也说下

 引导期间内核选项 __setup
用__setup宏注册关键字 
net/core/dev.c 比较常见的netdev关键字 ,在系统启动时候的参数

点击(此处)折叠或打开

  1. /*
  2.  * Saves at boot time configured settings for any netdevice.
  3.  */
  4. int __init netdev_boot_setup(char *str)
  5. {
  6.     int ints[5];
  7.     struct ifmap map;

  8.     str = get_options(str, ARRAY_SIZE(ints), ints);
  9.     if (!str || !*str)
  10.         return 0;

  11.     /* Save settings */
  12.     memset(&map, 0, sizeof(map));
  13.     if (ints[0] > 0)
  14.         map.irq = ints[1];
  15.     if (ints[0] > 1)
  16.         map.base_addr = ints[2];
  17.     if (ints[0] > 2)
  18.         map.mem_start = ints[3];
  19.     if (ints[0] > 3)
  20.         map.mem_end = ints[4];

  21.     /* Add new entry to the list */
  22.     return netdev_boot_setup_add(str, &map);
  23. }

  24. __setup("netdev=", netdev_boot_setup);
还有在启动时候,必须挂载根文件系统,指定root目录(如果谁pc机安装过linux系统,在启动的时候,有个选择启动的界面,可以打断,可以看到后面的参数;一般bootloader用grub ) init/do_mounts.c

点击(此处)折叠或打开

  1. static int __init root_dev_setup(char *line)
  2. {
  3.     strlcpy(saved_root_name, line, sizeof(saved_root_name));
  4.     return 1;
  5. }

  6. __setup("root=", root_dev_setup);

点击(此处)折叠或打开

  1. static int __init readonly(char *str)
  2. {
  3.     if (*str)
  4.         return 0;
  5.     root_mountflags |= MS_RDONLY;
  6.     return 1;
  7. }

  8. static int __init readwrite(char *str)
  9. {
  10.     if (*str)
  11.         return 0;
  12.     root_mountflags &= ~MS_RDONLY;
  13.     return 1;
  14. }

  15. __setup("ro", readonly);
  16. __setup("rw", readwrite);

我们看看它是如何调用的. 
在init/main.c 中 
asmlinkage void __init start_kernel(void)
{

...
printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
  __stop___param - __start___param,
  &unknown_bootoption);

...

} 这里我们看到启动的时候传递的参数会在这里解析。我们继续回到init.h

点击(此处)折叠或打开

  1. struct obs_kernel_param {
  2.     const char *str;
  3.     int (*setup_func)(char *);
  4.     int early;
  5. };

  6. /*
  7.  * Only for really core code. See moduleparam.h for the normal way.
  8.  *
  9.  * Force the alignment so the compiler doesn't space elements of the
  10.  * obs_kernel_param "array" too far apart in .init.setup.
  11.  */
  12. #define __setup_param(str, unique_id, fn, early)            \
  13.     static const char __setup_str_##unique_id[] __initconst    \
  14.         __aligned(1) = str; \
  15.     static struct obs_kernel_param __setup_##unique_id    \
  16.         __used __section(.init.setup)            \
  17.         __attribute__((aligned((sizeof(long)))))    \
  18.         = { __setup_str_##unique_id, fn, early }

  19. #define __setup(str, fn)                    \
  20.     __setup_param(str, fn, fn, 0)

  21. /* NOTE: fn is as per module_param, not Emits warning if fn
  22.  * returns non-zero. */
  23. #define early_param(str, fn)                    \
  24.     __setup_param(str, fn, fn, 1)

注册的时候有两个宏,__setup, early_param  当然既然带个early自然会比__setup注册的处理的早. 也就是我们看到start_kernel里两个函数的先后顺序.
我们就把宏展开:
本质是注册了struct obs_kernel_param 并把它放到了内存节区 .init.setup ,关于内存节区前面我们讲过。当然如果注册的关键字被编译成模块,那么这个宏扩展为空. 
我们先看early解析:

点击(此处)折叠或打开

  1. /* Check for early params. */
  2. static int __init do_early_param(char *param, char *val)
  3. {
  4.     struct obs_kernel_param *p;

  5.     for (p = __setup_start; p __setup_end; p++) {
  6.         if ((p->early && strcmp(param, p->str) == 0) ||
  7.          (strcmp(param, "console") == 0 &&
  8.          strcmp(p->str, "earlycon") == 0)
  9.         ) {
  10.             if (p->setup_func(val) != 0)
  11.                 printk(KERN_WARNING
  12.                  "Malformed early option '%s'\n", param);
  13.         }
  14.     }
  15.     /* We accept everything at this stage. */
  16.     return 0;
  17. }
include/asm-generic/vmlinux-lds.h

点击(此处)折叠或打开

  1. #define INIT_SETUP(initsetup_align)                    \
  2.         . = ALIGN(initsetup_align);                \
  3.         VMLINUX_SYMBOL(__setup_start) = .;            \
  4.         *(.init.setup)                        \
  5.         VMLINUX_SYMBOL(__setup_end) = .;
就是查询节区.init.setup ,如果查到就调用 p - > setup_func ( val )
那么正常注册的关键字呢?

点击(此处)折叠或打开

  1. parse_args("Booting kernel", static_command_line, __start___param,
  2.          __stop___param - __start___param,
  3.          &unknown_bootoption);
我们注意看第三个参数.那么__start_param在哪里呢?没见过吧^^ 
include/asm-generic/vmlinux-lds.h :

点击(此处)折叠或打开

  1. /* Built-in module parameters. */                \
  2.     __param : AT(ADDR(__param) - LOAD_OFFSET) {            \
  3.         VMLINUX_SYMBOL(__start___param) = .;            \
  4.         *(__param)                        \
  5.         VMLINUX_SYMBOL(__stop___param) = .;            \
  6.         . = ALIGN((align));                    \
  7.         VMLINUX_SYMBOL(__end_rodata) = .;

这里查询__param ,如果找到赋值并返回.

点击(此处)折叠或打开

  1. /* Args looks like "foo=bar,bar2 baz=fuz wiz". */
  2. int parse_args(const char *name,
  3.      char *args,
  4.      struct kernel_param *params,
  5.      unsigned num,
  6.      int (*unknown)(char *param, char *val))
  7. {
  8.     char *param, *val;

  9.     DEBUGP("Parsing ARGS: %s\n", args);

  10.     /* Chew leading spaces */
  11.     while (isspace(*args))
  12.         args++;

  13.     while (*args) {
  14.         int ret;
  15.         int irq_was_disabled;

  16.         args = next_arg(args, &param, &val);
  17.         irq_was_disabled = irqs_disabled();
  18.         ret = parse_one(param, val, params, num, unknown);
  19.         if (irq_was_disabled && !irqs_disabled()) {
  20.             printk(KERN_WARNING "parse_args(): option '%s' enabled "
  21.                     "irq's!\n", param);
  22.         }
  23.         switch (ret) {
  24.         case -ENOENT:
  25.             printk(KERN_ERR "%s: Unknown parameter `%s'\n",
  26.              name, param);
  27.             return ret;
  28.         case -ENOSPC:
  29.             printk(KERN_ERR
  30.              "%s: `%s' too large for parameter `%s'\n",
  31.              name, val ?: "", param);
  32.             return ret;
  33.         case 0:
  34.             break;
  35.         default:
  36.             printk(KERN_ERR
  37.              "%s: `%s' invalid for parameter `%s'\n",
  38.              name, val ?: "", param);
  39.             return ret;
  40.         }
  41.     }

  42.     /* All parsed OK. */
  43.     return 0;
  44. }











相关文章
|
3月前
|
安全 调度 开发者
探索操作系统的心脏:现代内核架构与挑战
【10月更文挑战第7天】 本文深入探讨了现代操作系统内核的复杂性和功能性,从微观角度剖析了内核在系统运行中的核心作用及其面临的主要技术挑战。通过浅显易懂的语言解释专业概念,旨在为读者提供一个关于操作系统内核的全面视角。
55 2
|
1月前
|
存储 Linux API
深入探索Android系统架构:从内核到应用层的全面解析
本文旨在为读者提供一份详尽的Android系统架构分析,从底层的Linux内核到顶层的应用程序框架。我们将探讨Android系统的模块化设计、各层之间的交互机制以及它们如何共同协作以支持丰富多样的应用生态。通过本篇文章,开发者和爱好者可以更深入理解Android平台的工作原理,从而优化开发流程和提升应用性能。
|
1月前
|
SQL 存储 关系型数据库
MySQL进阶突击系列(01)一条简单SQL搞懂MySQL架构原理 | 含实用命令参数集
本文从MySQL的架构原理出发,详细介绍其SQL查询的全过程,涵盖客户端发起SQL查询、服务端SQL接口、解析器、优化器、存储引擎及日志数据等内容。同时提供了MySQL常用的管理命令参数集,帮助读者深入了解MySQL的技术细节和优化方法。
|
2月前
|
机器学习/深度学习 自然语言处理 物联网
NeurIPS 2024 Oral:小参数,大作为!揭秘非对称 LoRA 架构的高效性能
近期,一篇题为《\model~: 非对称LoRA架构实现高效微调》的论文被NeurIPS 2024接收为口头报告,该研究提出了一种创新的非对称LoRA架构,旨在解决大型语言模型(LLMs)在保持高性能的同时提高训练和部署效率的问题。通过引入共享A矩阵和多个B矩阵,\model~不仅提高了参数效率,还在多个数据集上展示了超越现有PEFT方法的性能,尤其是在多任务域和复杂数据集上的表现尤为突出。此架构还有效减少了训练能耗和延迟,为LLMs的高效应用提供了新思路。
46 4
|
2月前
|
架构师 数据挖掘 开发者
架构师的真内核
本文旨在帮助大家深入理解技术、架构和团队领导力的本质,从而获得持续成长的方法。欢迎在文末留言,你觉得架构师需要具备的核心能力是什么?
|
2月前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
2月前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
89 2
|
3月前
|
存储 资源调度 算法
操作系统的心脏:深入理解内核架构与机制####
【10月更文挑战第16天】 本文旨在揭开操作系统最神秘的面纱——内核,通过剖析其架构设计与关键机制,引领读者一窥究竟。在这篇探索之旅中,我们将深入浅出地讨论内核的基本构成、进程管理的智慧、内存分配的策略,以及那至关重要的系统调用接口,揭示它们是如何协同工作,支撑起现代计算机系统的高效运行。这既是一次技术的深潜,也是对“看不见的手”调控数字世界的深刻理解。 ####
74 3
|
3月前
|
架构师 数据挖掘 UED
什么才是架构师的真内核?
技术架构师是在技术领域扮演着关键角色的专业人员。他们在业务需求分析、项目实施、技术架构治理等多个环节中发挥着重要的作用。 技术架构师不仅需要具备高超的专业技能,还需要具备良好的系统思维和认知心态。他们要能在宏观层面上进行技术架构的规划和治理,同时也要在微观层面上带领团队进行业务项目的交付实施。技术架构师是技术人从最初的研发编码,到成长为技术团队的核心骨干、技术主管、高阶技术主管,甚至是技术 CTO 的关键一步,如图 10-1 所示。
412 0
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
Tokenformer:基于参数标记化的高效可扩展Transformer架构
本文是对发表于arXiv的论文 "TOKENFORMER: RETHINKING TRANSFORMER SCALING WITH TOKENIZED MODEL PARAMETERS" 的深入解读与扩展分析。主要探讨了一种革新性的Transformer架构设计方案,该方案通过参数标记化实现了模型的高效扩展和计算优化。
238 0

热门文章

最新文章