1. __setup()介绍

static __init int dma_debug_cmdline(char *str)
{if (!str)return -EINVAL;if (strncmp(str, "off", 3) == 0) {pr_info("debugging disabled on kernel command line\n");global_disable = true;}return 1;
}static __init int dma_debug_entries_cmdline(char *str)
{if (!str)return -EINVAL;if (!get_option(&str, &nr_prealloc_entries))nr_prealloc_entries = PREALLOC_DMA_DEBUG_ENTRIES;return 1;
}__setup("dma_debug=", dma_debug_cmdline);
__setup("dma_debug_entries=", dma_debug_entries_cmdline);

我们经常可以在一些模块中看到__setup(),模块利用__setup()可以解析命令行传递的参数信息,进而对模块功能进行定制化的配置。
如上所示:__setup(“dma_debug=”, dma_debug_cmdline);
假设cmdline中含有dma_debug=off内容,则会将off作为入参传递到dma_debug_cmdline()函数进行解析。

2. 实现原理

2.1 __setup()宏展开

__setup()是一个宏,我们将其定义展开,如下所示:

/** NOTE: __setup functions return values:* @fn returns 1 (or non-zero) if the option argument is "handled"* and returns 0 if the option argument is "not handled".*/
#define __setup(str, fn)                        \__setup_param(str, fn, fn, 0)/** Only for really core code.  See moduleparam.h for the normal way.** Force the alignment so the compiler doesn't space elements of the* obs_kernel_param "array" too far apart in .init.setup.*/
#define __setup_param(str, unique_id, fn, early)            \static const char __setup_str_##unique_id[] __initconst        \__aligned(1) = str;                   \static struct obs_kernel_param __setup_##unique_id     \__used __section(".init.setup")              \__aligned(__alignof__(struct obs_kernel_param))        \= { __setup_str_##unique_id, fn, early }

以__setup(“dma_debug=”, dma_debug_cmdline);为例,我们进行代入:

static const char __setup_str_dma_debug_cmdline[] __initconst _aligned(1) = "dma_debug=";
static struct obs_kernel_param __setup_dma_debug_cmdline __used __section(".init.setup") __aligned(__alignof__(struct obs_kernel_param)) = { __setup_dma_debug_cmdline, dma_debug_cmdline, 0 }

其实这个相当于做了一件事情,只是在init.setup段中,定义了一个struct obs_kernel_param结构体,并为其赋值而已,赋值后的struct obs_kernel_param结构体内容是这样的:

struct obs_kernel_param {const char *str;int (*setup_func)(char *);int early;
};
obs_kernel_param.str = "dma_debug=";
obs_kernel_param.setup_func = dma_debug_cmdline;
obs_kernel_param.early = 0;

2.2 init.setup段位置

#define INIT_SETUP(initsetup_align)                  \. = ALIGN(initsetup_align);               \__setup_start = .;                    \KEEP(*(.init.setup))                   \__setup_end = .;

由定义可知:init.setup段位于__setup_start - __setup_end 之间

2.3 __setup(str, fn) fn何时被调用

让我们一步步从内核启动的开始进行跟踪:

start_kernel()-> parse_args()-> parse_one()-> unknown_bootoption()

因为__setup()设置的obs_kernel_param.early = 0,因此不会进入start_kernel()->parse_early_param()中去解析,另外由于__setup()将obs_kernel_param结构体保存在__setup_start - __setup_end之中,因此在parse_one()函数解析时:

static int parse_one(char *param,char *val,const char *doing,const struct kernel_param *params,unsigned num_params,s16 min_level,s16 max_level,void *arg,int (*handle_unknown)(char *param, char *val,const char *doing, void *arg))
{unsigned int i;int err;/* Find parameter */for (i = 0; i < num_params; i++) {if (parameq(param, params[i].name)) {if (params[i].level < min_level|| params[i].level > max_level)return 0;/* No one handled NULL, so do it here. */if (!val &&!(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG))return -EINVAL;pr_debug("handling %s with %p\n", param,params[i].ops->set);kernel_param_lock(params[i].mod);if (param_check_unsafe(&params[i]))err = params[i].ops->set(val, &params[i]);elseerr = -EPERM;kernel_param_unlock(params[i].mod);return err;}}

这部分代码不会执行,最终会来到

 if (handle_unknown) {pr_debug("doing %s: %s='%s'\n", doing, param, val);return handle_unknown(param, val, doing, arg);}

这里进行处理,而handle_unknown函数指针的值是&unknown_bootoption,因此最终__setup()的内容由unknown_bootoption()函数进行处理。

现在我们来看一些unknown_bootoption()函数的实现细节:

/** Unknown boot options get handed to init, unless they look like* unused parameters (modprobe will find them in /proc/cmdline).*/
static int __init unknown_bootoption(char *param, char *val,const char *unused, void *arg)
{size_t len = strlen(param);repair_env_string(param, val);/* Handle obsolete-style parameters */if (obsolete_checksetup(param))return 0;/* Unused module parameter. */if (strnchr(param, len, '.'))return 0;if (panic_later)return 0;if (val) {/* Environment option */unsigned int i;for (i = 0; envp_init[i]; i++) {if (i == MAX_INIT_ENVS) {panic_later = "env";panic_param = param;}if (!strncmp(param, envp_init[i], len+1))break;}envp_init[i] = param;} else {/* Command line option */unsigned int i;for (i = 0; argv_init[i]; i++) {if (i == MAX_INIT_ARGS) {panic_later = "init";panic_param = param;}}argv_init[i] = param;}return 0;
}

unknown_bootoption()函数通过调用obsolete_checksetup()函数进行进一步处理:

static bool __init obsolete_checksetup(char *line)
{const struct obs_kernel_param *p;bool had_early_param = false;p = __setup_start;do {int n = strlen(p->str);if (parameqn(line, p->str, n)) {if (p->early) {/* Already done in parse_early_param?* (Needs exact match on param part).* Keep iterating, as we can have early* params and __setups of same names 8( */if (line[n] == '\0' || line[n] == '=')had_early_param = true;} else if (!p->setup_func) {pr_warn("Parameter %s is obsolete, ignored\n",p->str);return true;} else if (p->setup_func(line + n))return true;}p++;} while (p < __setup_end);return had_early_param;
}

obsolete_checksetup()函数中可以看到,程序将__setup_start-__setup_end中保存的所有obs_kernel_param 结构体内容进行遍历,使用param(param是从cmdline中获取到的参数名称)进行匹配。如果paramobs_kernel_param.str相等,则会调用obs_kernel_param.setup_func()函数进行处理。

对于cmdline中包含dma_debug=off内容时,param=“dma_debug=”,此时就会将"off"作为参数传递到dma_debug_cmdline()函数中进行解析。

3. 其他细节

上述以__setup(“dma_debug=”, dma_debug_cmdline);为例,分析了整个解析过程。

但是还有一些细节需要注意,__setup(str, fn),对于fn函数的返回值是有要求的,当返回为1时,表示传入的参数可以被fn函数解析,如果返回为0,则代表传入的参数格式错误,无法被fn函数解析。

返回值为0的参数,会被内容所记录,流程如下:
如果fn函数的返回值为0,则obsolete_checksetup()函数的返回值为false,则会由unknown_bootoption()函数进行进一步处理:

/** Unknown boot options get handed to init, unless they look like* unused parameters (modprobe will find them in /proc/cmdline).*/
static int __init unknown_bootoption(char *param, char *val,const char *unused, void *arg)
{size_t len = strlen(param);repair_env_string(param, val);/* Handle obsolete-style parameters */if (obsolete_checksetup(param))return 0;/* Unused module parameter. */if (strnchr(param, len, '.'))return 0;if (panic_later)return 0;if (val) {/* Environment option */unsigned int i;for (i = 0; envp_init[i]; i++) {if (i == MAX_INIT_ENVS) {panic_later = "env";panic_param = param;}if (!strncmp(param, envp_init[i], len+1))break;}envp_init[i] = param;} else {/* Command line option */unsigned int i;for (i = 0; argv_init[i]; i++) {if (i == MAX_INIT_ARGS) {panic_later = "init";panic_param = param;}}argv_init[i] = param;}return 0;
}

如果传递的参数值不为NULL,则会将param信息保存到envp_init[]字符数组中,如果如果传递的参数值为NULL,则会将param信息保存到argv_init[]字符数组中。

最终这些保存到字符数组中的内容,则会由start_kernel()->print_unknown_bootoptions()函数进行打印输出。

static void __init print_unknown_bootoptions(void)
{char *unknown_options;char *end;const char *const *p;size_t len;if (panic_later || (!argv_init[1] && !envp_init[2]))return;/** Determine how many options we have to print out, plus a space* before each*/len = 1; /* null terminator */for (p = &argv_init[1]; *p; p++) {len++;len += strlen(*p);}for (p = &envp_init[2]; *p; p++) {len++;len += strlen(*p);}unknown_options = memblock_alloc(len, SMP_CACHE_BYTES);if (!unknown_options) {pr_err("%s: Failed to allocate %zu bytes\n",__func__, len);return;}end = unknown_options;for (p = &argv_init[1]; *p; p++)end += sprintf(end, " %s", *p);for (p = &envp_init[2]; *p; p++)end += sprintf(end, " %s", *p);/* Start at unknown_options[1] to skip the initial space */pr_notice("Unknown kernel command line parameters \"%s\", will be passed to user space.\n",&unknown_options[1]);memblock_free(unknown_options, len);
}

因此__setup(str, fn) fn的返回值如果非1的话,传入的参数信息则会在出现在Unknown kernel command line parameters日志中。

感谢大家的浏览,本文为博主原创,未经允许不可转载。

Linux中__setup()实现原理以及源码分析相关推荐

  1. Linux中mknod命令实现原理以及源码分析

    本篇文章以mknod创建字符设备文件进行讲解 字符设备驱动的Demo例子可参考该篇文章 Linux 编写简单驱动并测试 1. mknod 命令 mknod /dev/hello c 520 0 该命令 ...

  2. Linux中C语言标准库glibc源码下载

    在这篇文章理清gcc.libc.glibc.libc++.libstdc++的关系,我们大概理解了libc,glibc之间的一些关系. 下面我们就开了解一些Linux中C语言标准库glibc源码. 在 ...

  3. ConcurrentHashMap实现原理及源码分析

    ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),Con ...

  4. concurrenthashmap_ConcurrentHashMap实现原理及源码分析

    ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),Con ...

  5. netty中的future和promise源码分析(二)

    前面一篇netty中的future和promise源码分析(一)中对future进行了重点分析,接下来讲一讲promise. promise是可写的future,从future的分析中可以发现在其中没 ...

  6. SIFT原理与源码分析:DoG尺度空间构造

    <SIFT原理与源码分析>系列文章索引:http://blog.csdn.net/xiaowei_cqu/article/details/8069548 尺度空间理论 自然界中的物体随着观 ...

  7. (转)Linux设备驱动之HID驱动 源码分析

    //Linux设备驱动之HID驱动 源码分析 http://blog.chinaunix.net/uid-20543183-id-1930836.html HID是Human Interface De ...

  8. 深入理解Spark 2.1 Core (十二):TimSort 的原理与源码分析

    在博文<深入理解Spark 2.1 Core (十):Shuffle Map 端的原理与源码分析 >中我们提到了: 使用Sort等对数据进行排序,其中用到了TimSort 这篇博文我们就来 ...

  9. 深入理解Spark 2.1 Core (十一):Shuffle Reduce 端的原理与源码分析

    我们曾经在<深入理解Spark 2.1 Core (一):RDD的原理与源码分析 >讲解过: 为了有效地实现容错,RDD提供了一种高度受限的共享内存,即RDD是只读的,并且只能通过其他RD ...

最新文章

  1. 读书--编写高质量代码 改善C#程序的157个建议2
  2. dom4j解析xml实例(2)
  3. C++radix sort基数排序的实现算法之一(附完整源码)
  4. 透过【百度地图API】分析双闭包问题
  5. composer安装及使用
  6. 使用Sublime Text 3做Python开发
  7. MVVM项目中的动态DataGrid单元样式
  8. 加密软件漏洞评测系统_【E周道】Elasticsearch泄露12亿用户数据 开源VNC存在37个漏洞...
  9. 分布式存储系统学习笔记(三)—分布式键值系统(2)—淘宝Tair
  10. Pausing and Resuming an Activity
  11. android电视盒刷机工具,android电视盒如何刷机
  12. 时频分析之STFT:短时傅里叶变换的原理与代码实现(非调用Matlab API)
  13. 亿阳信通面试题2006.12.15
  14. sql 按照天环比_同比环比sql实例
  15. 揭秘支付机构【备付金】管理过程
  16. 《编译原理》求短语,直接短语,句柄,素短语,最左素短语 - 例题解析
  17. 别样肉客首次针对中国市场独家研发创新植物性猪肉糜产品
  18. 启用DoH(DNS-over HTTPS)在Windows、Android、IOS平台
  19. ORACLE之热备份
  20. iOS动画 Masonry约束弹框动画animateWithDuration:

热门文章

  1. 西安市经开区自行车管理系统设计摘要
  2. 从开发者的角度比较Kubernetes和Cloud Foundry
  3. processing创意图形代码_Processing练习-Self Sketch - TAI CHI
  4. Ubuntu12.04 安装Skype
  5. SpringCloud集成微信支付
  6. 计算机应用技术办公室自动化,办公室自动化的计算机处理技术应用研究
  7. 云桌面可通过以下三种终端设备连接使用的
  8. 小程序底部tabbar加数字标
  9. kwebio/kweb-core:面向后端的轻量级 Kotlin Web 框架
  10. MYSQL的一知半解