linux中的__setup详细解析和使用场景

版本linux3.18

static int __init myfunc(void)

{

……

}

__setup("myfuncstr",myfunc);

__init 宏  __section(.init.text)

__setup() 是一个宏定义,在include/linux/init.h这个文件中.

struct obs_kernel_param { 
const char *str;

int (*setup_func)(char *);

int early;

};

extern struct kernel_param __setup_start, __setup_end;

#define __setup(str, fn) \ 
static char __setup_str_##fn[] __initdata = str; \ 
static struct kernel_param __setup_##fn __attribute__((unused)) __initsetup = { __setup_str_##fn, fn }

在这个情景中作了替换是这样的

static char __setup_str_myfunc[] = "myfunc"; 
static struct kernel_param __setup_myfunc = { __setup_str_myfunc, myfunc}

vmlinux.lds这个关于ld 链接器的脚本文件有这样的一段

.init.data : {

*(.init.data)  .=ALIGN(8);

//输入段为.init.data   8字节对齐

*(.init.rodata);

//输入段为.init.rodata

__setup_start = .;

//.表示当前的offset,相当于该变量在vmlix镜像中的文件偏移

*(.setup.init)

__setup_end = .;

……

}

这里的意思就是__setup_start是一个节的开始,而__setup_end是一个节的结束,这个节的名称是.init.setup, 
这个你可以用readelf -a这个来看一下你的vmlinux这个文件,

可以看到.init.setup就在.init.data的节中, 数据结构数组,一个就是str,一个就是setup_func,

与我前面的说法相一致,那具体是什么呢,就是一个在.init.data节中存储的

字符串-----__initdata是一个宏,就是(__attribute__ ((__section__ (".init.data")))), 所以你可以.init.data在vmlinux中的

在文件中的偏移量与加载的的虚拟地址偏移量相减就可以得到,

举个例子,所有的这些都是用readelf 与od 命令得到的

我现在用的内核版本,它的.init.setup的节在0x53d898 的文件偏移处.

addr          offset      size

[16] .init.text  PROGBITS c05192e0  5212e0 01b7c0 00 AX  0 0 32

[20] .init.data PROGBITS c0535898 53d898 008698 00 WA 0 0 4

再查找myfunc和init.setup在vmlinux被映射为内存地址,

74733:c053d660    0     NOTYPE    GLOBAL  DEFAULT   20  __setup_start

74733:c053db40    0     NOTYPE    GLOBAL  DEFAULT   20  __setup_end

77840: c051ef0c  16     FUNC         LOCAL     DEFAULT   16     myfunc

15506:c053d7c8  12    OBJECT     LOCAL     DEFAULT   20   __setup_myfunc

15507:c053c0a6  16    OBJECT     LOCAL     DEFAULT   20   __setup_str_myfunc

0xc051ef0c <myfunc>:@0xc0514c78

//ARM UNWIND IDX 信息

这就可以知道myfunc所在的位置就是0xc051ef0c ,这就是它的虚拟映射地址

__setup_str_myfunc就是myfuncstr字符串的映射地址

ADDR{__setup_str_myfunc}(0xc053c0a6) -  ADDR{.init.data}(0xc0535898) + FILEOFF{.init.data}(0x5212e0)=0x5440a6

16进制的方式打开vmlinux查看偏移量0x5440a6得到的字符串正是myfuncstr

同理计算__setup_myfunc对应的vmlinux文件偏移量是0x5457c8

vmlinux偏移量0x5457c8位置的值为A6 C0   53  C0   0C  EF  51   C0

与ADDR{myfunc}0xc051ef0c对应和ADDR{__setup_str_myfunc}0xc53c0a6对应

//补充///

文章开始处调用__setup的使用场景

而 obsolete_checksetup 这个函数的工作就是遍历 .init.setup 段,

根据bootloader传入的命令行参数,搜索结构体 obs_kernel_param 中 str 的变量符合"root="、"init="、等字符串的,

调用对应的 setup_func 函数。至于如何解析命令行参数,是在 parse_args 中做的工作,大家可以自行分析。

static int __init obsolete_checksetup(char *line)
{
const struct obs_kernel_param *p;
int had_early_param = 0;

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 = 1;
} else if (!p->setup_func) {
pr_warn("Parameter %s is obsolete, ignored\n",
p->str);
return 1;
} else if (p->setup_func(line + n))
return 1;
}
p++;
} while (p < __setup_end);

return had_early_param;
}

//补充///

参见include/linux/init.h和vmlinux.lds

1)

所有标识为__init的函数在链接的时候都放在.init.text这个区段内, 
在这个区段中,函数的摆放顺序是和链接的顺序有关的,是不确定的。

2) 
所有的__init函数在区段.initcall.init中还保存了一份函数指针, 
在初始化时内核会通过这些函数指针调用这些__init函数指针, 
并在整个初始化完成后,释放整个init区段(包括.init.text,.initcall.init等),

注意,这些函数在内核初始化过程中的调用顺序只和这里的函数指针的顺序有关, 
和1)中所述的这些函数本身在.init.text区段中的顺序无关。

在2.4内核中,这些函数指针的顺序也是和链接的顺序有关的,是不确定的。

在2.6内核中,initcall.init区段又分成7个子区段,分别是 
.initcall1.init 
.initcall2.init 
.initcall3.init 
.initcall4.init 
.initcall5.init 
.initcall6.init 
.initcall7.init

当需要把函数fn放到.initcall1.init区段时,只要声明 
core_initcall(fn); 
即可。

其他的各个区段的定义方法分别是: 
core_initcall(fn) --->.initcall1.init 
postcore_initcall(fn) --->.initcall2.init 
arch_initcall(fn) --->.initcall3.init 
subsys_initcall(fn) --->.initcall4.init 
fs_initcall(fn) --->.initcall5.init 
device_initcall(fn) --->.initcall6.init 
late_initcall(fn) --->.initcall7.init

而与2.4兼容的initcall(fn)则等价于device_initcall(fn)。

各个子区段之间的顺序是确定的,即先调用.initcall1.init中的函数指针 
再调用.initcall2.init中的函数指针,等等。 
而在每个子区段中的函数指针的顺序是和链接顺序相关的,是不确定的。

在内核中,不同的init函数被放在不同的子区段中,因此也就决定了它们的调用顺序。 
这样也就解决了一些init函数之间必须保证一定的调用顺序的问题。

3) 
这些顺序和make dep没有关系。 
(转自http://blog.csdn.net/jifengszf/archive/2009/04/22/4100193.aspx)
内核组件用__setup宏来注册关键字及相关联的处理函数,__setup宏在include/linux/init.h中定义,其原型如下:
__setup(string, function_handler)
其 中:string是关键字,function_handler是关联处理函数。__setup只是告诉内核在启动时输入串中含有string时,内核要去 执行function_handler。String必须以“=”符结束以使parse_args更方便解析。紧随“=”后的任何文本都会作为输入传给 function_handler。
下面的例子来自于net/core/dev.c,其中netdev_boot_setup作为处理程序被注册给“netdev=”关键字:
__setup("netdev=", netdev_boot_setup);
不 同的关键字可以注册相同的处理函数,例如在net/ethernet/eth.c中为“ether =”关键字注册了同样的处理函数 netdev_boot_setup。当代码作为模块被编译时,__setup宏被忽视,你可以在include/linux/init.h中看到 __setup宏是怎样变化的,不管后续包含它的文件是否是模块,include/linux/init.h都是独立的。
start_kernel两次调用parse_args解析启动配置字符串的原因是启动选项事实上分为两类,且每次调用值能够兼顾到其中一类:

缺省选项:
绝大多数选项归于此类,这些选项由__setup宏定义并在第二次调用parse_args时处理。
先期(处理)选项:
在 内核启动阶段,有些选项要在其它选项之前被处理,内核提供了early_param宏以代替__setup宏申明此类选项。这些选项由 parse_early_params函数解析。early_param宏和__setup宏仅有的不同就是前者设置了一个特殊标志让内核能够区分两种不 同的状况。这个标志是我们将在“.init.setup内存区”小节中看到的obs_kernel_param结构的一部分。
启动时选项在内核 2.6中的处理方式已经改变,但并非所有的内核代码都因此而更新。在最近一次改变之前,还仅用__setup宏。因此,遗留下来将被更新的代码现在使用 __obsolete_setup宏。但用户用__obsolete_setup宏定义的选项给内核时,内核打印一条警告消息说明它已是废弃状态,并提供 一个文件指针和随后被公告的源代码行信息。

linux中的__setup的作用相关推荐

  1. Linux中popen函数的作用小结

    概述 popen()函数通过创建一个管道,调用fork()产生一个子进程,执行一个shell以运行命令来开启一个进程.这篇文章重点给大家介绍Linux中popen函数的作用,感兴趣的朋友一起看看吧 p ...

  2. linux命令touch意思,Linux中touch命令的作用是什么

    Linux中touch命令的作用是什么 发布时间:2021-03-12 16:12:58 来源:亿速云 阅读:123 作者:Leah 本篇文章为大家展示了Linux中touch命令的作用是什么,内容简 ...

  3. Linux中echo $命令的作用

    Linux中echo $命令的作用 Linux中echo $命令的作用 echo $$ 返回登录shell的PID echo $? 返回上一个命令的状态,0表示没有错误,其它任何值表明有错误 echo ...

  4. linux中的环境变量作用

    告诉机器,你输入的命令到哪里去执行.指定命令执行的路径. shell必须搜索系统来找到对应的程序. PATH环境变量定义了用于进行命令和程序查找的目录. Linux是一个多用户多任务的操作系统,可以在 ...

  5. linux中whoami命令的作用是,浅谈linux中的whoami与 who指令

    浅谈linux中的whoami与 who指令 whoami 功能说明: 显示用户名称 语法: whoami 补充说明: 显示自身的用户名称,本指令相当于执行  id -un 指令 whoami 与 w ...

  6. 简述Linux中Swap分区的作用,linux下的swap分区

    一.什么叫swap分区 swap分区,即交换区,swap空间的作用可简单描述为:当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用.那些被释放的空间可能来自一 ...

  7. linux cat命令的作用,Linux中cat命令的作用有哪些

    本期微子网络将为您带来Linux中的cat命令的功能.文章内容丰富,从专业角度分析叙述.看完这篇文章,希望你能有所收获. cat命令的目的是连接文件或标准输入并打印它们.此命令通常用于显示文件内容,或 ...

  8. linux中facl有什么作用,在Linux中使用setfacl后权限被拒绝

    有3个用户:A,B,C. A在他的主文件夹/ home / A / mydir中有mydir B可以读/ home / A内容 C可以读写/ home / A内容 我试过这样的方式: setfacl ...

  9. linux中touch命令的作用,Linux中touch命令的作用是什么

    今天微子网络向你展示了触摸命令在Linux中的功能.内容简洁易懂,一定会让你眼前一亮.希望通过这篇文章的详细介绍,你能有所收获. linux的Touch命令不常用,但在使用make时可能会用到,mak ...

  10. linux中$符号有什么作用,linux中的“$”符号表示什么

    linux中的"$"符号表示什么 发布时间:2020-07-03 09:43:09 来源:亿速云 阅读:124 作者:Leah linux中的"$"符号表示什么 ...

最新文章

  1. Office 365发送超大附件
  2. python操作Elasticsearch7.17.0
  3. ionic 获取input的值
  4. shellcode 编码技术
  5. Docker启动tomcat,访问首页出现404错误
  6. html里下拉标记,HTML: select 标签
  7. cocos2d-x 旅程開始--(实现单击与长按)
  8. [转载] 七龙珠第一部——第021话 克林危险
  9. 力扣——在排序数组中查找元素的第一个和最后一个位置
  10. C语言实现简单小游戏
  11. 药师经(生字注音图文版)
  12. 贸易相关术语[C-E]
  13. 如何将mp3合并在一起?
  14. Python 决策树计算熵、gini系数、误分率
  15. 数据库的隔离级别以及锁的关系的思考
  16. 【渲染】解决三维出图黑白边缘溢出问题:直通(STRAIGHT)与预乘(PREMULT)ALPHA剖析
  17. 如何用Java读取单元格的数据_Java读取Excel中的单元格数据
  18. 科幻科技对话:机器人实在不能变得像人一样愚蠢
  19. OpenCV-Python学习资源
  20. 终于有人把TCP/IP讲的明明白白了,搞懂真的不难,只需要看这一篇就够了

热门文章

  1. IE浏览器卡死的问题
  2. 平面设计内容包括什么,平面设计具体包括哪些内容
  3. TOM邮箱6.0版新功能体验—全新的交互设计
  4. ios中自定义相机_在iOS中制作自定义相机
  5. python在线编辑器编译excel_如何利用ONLYOFFICE将在线文档编辑器集成到Python Web应用程序中?...
  6. Nvidia NX平台控制台调试串口修改调试记录
  7. ie9 html5 web worker,HTML5之殇
  8. 【5G核心网】5GC核心网之网元UDR
  9. 画二元函数即三维图像的函数及matlab代码
  10. 好听的摇滚_好听的摇滚歌曲有哪些 十大最好听中国摇滚歌曲