本文转载自:http://blog.csdn.net/lanmanck/article/details/7613305

本文来自:

http://blog.chinaunix.net/uid-13798723-id-2871394.html

介绍的很详细,我也正好用了一次__setup(),蛮好用的:),今天回头看main.c,又搜索了一下,分享给大家:

__setup(

//做了一次完整的盗贼,我毫无修改的抄袭了一次,但有时间会做量身裁减的//
from   http://www.linuxforum.net/forum/printthread.php?Cat=&Board=linuxK&main=470221&type=thread
关于__setup 在内核中的作用 
问题::
比如在printk.c有这样一句 
__setup("console=",console_setup); 
还有,在main.c中 
__setup("root=",root_dev_setup); 
人家的精彩回答:
你的这个问题,我从google上查找到了一些资料,再结合内核源代码,就在这里把这个问题说的清楚一点. 
首先,这里有一个简短的回答, http://mail.nl.linux.org/kernelnewbies/2003-03/msg00043.html 
从这上面的意思是这里会从main.c 中的checksetup函数中运行,这个函数是这样的 
static int __init checksetup(char *line) 

struct kernel_param *p; 
p = &__setup_start; 
do { 
int n = strlen(p->str); 
if (!strncmp(line,p->str,n)) { 
if (p->setup_func(line+n)) 
return 1; 

p++; 
} while (p < &__setup_end); 
return 0; 

这里的意思是从__setup_start开始处到__setup_end处中查找一个数据结构,这个数据结构中有str与setup_func这两个数据成员变量. 
只要与这里面的str与输入的参数字符串相匹配,就会调用个这个字符串后面所指的内容, 
对于你这里所说的 __setup("console=",console_setup); 就是你在启动linux内核的时候如果有这么一个参数输入console=ttyS1,那内核就会 
把默认的tty定位为ttyS1,这个在consol_setup函数的字符串处理中完成,因为它最后是确定prefered_console的参数. 
那把这在这里实现这个的内容是这样的, 
__setup() 是一个宏定义,在include/linux/init.h这个文件中. 
struct kernel_param { 
const char *str; 
int (*setup_func)(char *); 
}; 
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_console_setup[] = "console="; 
static struct kernel_param __setup_console_setup = { __setup_str_console_setup, console_setup} 
这样你还可能不是很清楚,那你就要参考arch/i386/vmlinuz.lds这个关于ld 链接器的脚本文件有这样的一段 
__setup_start = .; 
.setup.init : { *(.setup.init) } 
__setup_end = .; 
这里的意思就是__setup_start是一个节的开始,而__setup_end是一个节的结束,这个节的名称是.setup,init, 
这个你可以用readelf -a这个来看一下你的vmlinux-2.4.20-8(后面的数字与你的内核版本有关)这个文件, 
可以看到有一个叫.setup.init的节,__setup_start就是指这个节的开始,那这个节中有什么内容呢,其实就是一个 
数据结构,一个就是str,一个就是setup_func,与我前面的说法相一致,那具体是什么呢,就是一个在.init.data节中存储的 
字符串-----__initdata是一个宏,就是(__attribute__ ((__section__ (".data.init")))), 所以你可以.data.init在vmlinux-2.4.20-8中的 
在文件中的偏移量与加载的的虚拟地址偏移量相减就可以得到, 
举个例子,所有的这些都是用readelf 与od 命令得到的 
我现在用的内核版本,它的.setup.init的节在0x26dd60的文件偏移处. 
[10] .data.init PROGBITS c0368040 268040 005d18 00 WA 0 0 32 
[11] .setup.init PROGBITS c036dd60 26dd60 0001b0 00 WA 0 0 4 
再查找console_setup在vmlinux-2.4.20-8所被映射为内存地址, 
840: c0355d40 343 FUNC LOCAL DEFAULT 9 console_setup 
这就可以知道了它所在的位置,就是0xc0355d40,这就是它的虚拟映射地址 
再用下面一条命令 
od --address-radix=x -t x4 vmlinux-2.4.20-8 |grep -A 20 26dd60 |head -20 | grep c0355d40 
可以得到 
26de40 c036943b c0355d10 c0369447 c0355d40 
很明显,这个函数的处理字符串在内存中的地址是0xc0369447,与前面得到的.data.init节在内存映射中的位置 
0xc0368040相减就是 0x1407,与.data.init在文件中的偏移量0x268040相加就得到0x269447 
这样用 
od --address-radix=x -a vmlinux-2.4.20-8 |grep -A 2 269440 
就可以得到下面的内容, 
269440 b l i n k = nul c o n s o l e = nul 
269450 r e s e r v e = nul nul nul nul nul nul nul nul 
269460 ` dc4 6 @ ` dc4 6 @ c p u f r e q = 
"console="这个值果真就在这里. 
(注:前面od 的选项 --address-radix= 表示的是显示文件偏移量的格式,默认下是o就是八进制, -t 表示显示文件二进制的形式
默认是o6 就是八进制的6位长,而-a表示显示的是字符串格式.) 
这是一点感受,与大家分享,希望大家提出宝贵意见. 
//补充///

参见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宏定义的选项给内核时,内核打印一条警告消息说明它已是废弃状态,并提供 一个文件指针和随后被公告的源代码行信息。
图7-1概述了几个宏之间的关系:它们都包裹了普通的__setup_param函数。

__setup 在内核中的作用【转】相关推荐

  1. initramfs 在内核中的作用与实现

    0x10 initramfs 简介 Linux 允许将一部分内存作为块设备 (RAM block device support).这通常见于完全运行于内存上的 Linux 的 live 发行版.Lin ...

  2. 宏EXPORT_SYMBOL在内核中的作用

    Module运行在kernel space,不能直接调用应用程序库,只能调用内核函数. EXPORT_SYMBOL的作用是把内核函数的符号导出,也就是将函数的入口地址导出. Module在运行的时候会 ...

  3. 内核中_init,_exit中的作用

    __init, __initdata等属性标志,是要把这种属性的代码放入目标文件的.init.text节,数据放入.init.data节──这一过程是通过编译内核时为相关目标平台提供了xxx.lds链 ...

  4. module_param 在内核编程中的作用

    module_param 在用户态下编程可以通过main()的来传递命令行参数,而编写一个内核模块则通过module_param()! module_param的作用 一.module_param 1 ...

  5. Linux内核中makefile有什么作用?深入解析makefile工作过程和原理

    Table of Contents Makefile 中的变量 常用的变量有以下几类: 1) 版本信息 2) CPU 体系结构:ARCH 3) 路径信息:TOPDIR, SUBDIRS 4) 内核组成 ...

  6. linux内核中的jiffies,Linux内核中的jiffies及其作用介绍及jiffies等相关函数详解

    在LINUX的时钟中断中涉及至二个全局变量一个是xtime,它是timeval数据结构变量,另一个则是jiffies,首先看timeval结构 struct timeval { time_t tv_s ...

  7. 一文讲解Linux内核中根文件系统挂载流程

    根文件系统的概念 根文件系统是控制权从linux内核转移到用户空间的一个桥梁.linux内核就类似于一个黑匣子,只向用户提供各种功能的接口,但是功能的具体实现不可见,用户程序通过对这些功能接口的不同整 ...

  8. 简单谈一点linux内核中套接字的bind机制--数据结构以及端口确定

    众所周知,创建一个套接字可以bind到一个特定的ip地址和端口,实际上套接字这一概念代表了TCP/IP协议栈的应用层标识,协议栈中的应用层就是通过一个ip地址和一个端口号标识的,当然这仅仅是对于TCP ...

  9. Linux内核分析--内核中的数据结构双向链表续【转】

    在解释完内核中的链表基本知识以后,下面解释链表的重要接口操作: 1. 声明和初始化 实际上Linux只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来的呢?让我们来看看LIST_H ...

最新文章

  1. python简单代码加法-CPython 源码中整数加法的实现
  2. Tkinter的Scrollba组件
  3. jsp mysql demo_利用JSP+MYSQL实现注册+登入的demo----0001
  4. ASP.NET Core 中断请求了解一下(翻译)
  5. 使用CXF 来发布一个 service
  6. 通过配置XML,使用TpiSyntaxAnalyzer语法分析,快速生成网页
  7. 【深度语义匹配模型 】原理篇一:表示型
  8. 2020 cr节目源_2020年四款最新直播软件汇总,频道多,画质流畅节目源超级稳定!...
  9. java,Hash冲突及解决办法
  10. 使用PHP+MYSQL搭建的一款直播电商源码和大家分享一下
  11. Angular纯前端实现table表格导出excel的xls格式
  12. 计算机在课程中的应用,计算机科学技术在计算机教育中的应用
  13. 三角形中的欧拉公式证明过程
  14. safenet 超级狗 加密狗
  15. 年薪百万的年轻人都是怎样生活的
  16. Web前端学习笔记——JavaScript之面向对象游戏案例:贪吃蛇
  17. Arduino与JavaScript开发实例-舵机驱动
  18. 程序员成长之路(Day 12)
  19. 安装VC,NTVDM CPU 遇到无效指令 --绝对能用的解决方法
  20. 计算机预测自己未来的相貌,AI算法预测未来相貌,准确率高达90%以上

热门文章

  1. wpf 实现实时毛玻璃(live blur)效果
  2. 做什么副业挣钱快?2021有哪些挣钱的副业?
  3. 基因序列相似度(LCS)
  4. 6岁学编程,10岁拒绝谷歌offer,神奇女孩要自己当CEO!永远别低估孩子的潜能…...
  5. 汇编语言实现冒泡法排序
  6. jsp内置对象之out对象常用方法
  7. C#创建及读取DAT文件案例
  8. 计算机语言送祝福,用计算机语言祝福大家新年快乐!
  9. 如何抠图?分享几个抠图小妙招
  10. Python计算机二级编程题真题及考点总结【纯干货】(下篇)