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

本文来自:

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

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

__setup(

//做了一次完整的盗贼,我毫无修改的抄袭了一次,但有时间会做量身裁减的//
关于__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. Linux内核中的jiffies及其作用介绍及jiffies等相关函数详解

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

  2. linux 驱动学习笔记02--应用实例:在内核中新增驱动代码目录和子目录

    下面来看一个综合实例,假设我们要在内核源代码 drivers 目录下为 ARM 体系结构新增如下用于 test driver 的树型目录:| --test  | -- cpu  | -- cpu.c ...

  3. [php-src]理解Php内核中的函数与INI

    内容均以php-5.6.14为例. 一. 函数结构 内核中定义一个php函数使用 PHP_FUNCTION 宏 包装,扩展也不例外,该宏在 ./main/php.h:343 有着一系列类似以 PHP ...

  4. Linux内核中双向链表的经典实现

    概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ...

  5. linux内核中jiffies的回绕问题【转】

    本文转载自:http://blog.csdn.net/yuanlulu/article/details/6019862 ======================================== ...

  6. linux内核中异步通信机制--信号处理机制【转】

    转自:http://blog.csdn.net/lu_embedded/article/details/51131663 什么是异步通信?很简单,一旦设备准备好,就主动通知应用程序,这种情况下应用程序 ...

  7. Linux内核中的GPIO系统之(3):pin controller driver代码分析

    一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...

  8. Apparmor——Linux内核中的强制访问控制系统

      AppArmor 因为最近在研究OJ(oline judge)后台的安全模块的实现,所以一直在研究Linux下沙箱的东西,同时发现了Apparmor可以提供访问控制. AppArmor(Appli ...

  9. Linux内核中流量控制

    linux内核中提供了流量控制的相关处理功能,相关代码在net/sched目录下:而应用层上的控制是通过iproute2软件包中的tc来实现, tc和sched的关系就好象iptables和netfi ...

随机推荐

  1. 关于hibernate中的 lazy="false“

    如上图所示,warehousePicked 中包含了warehousePicking 同时,数据库中warehousePicking存在数据 ,但是在debug 时warehousePicked的be ...

  2. Selenium加载Chrome/Firefox浏览器配置文件

    Selenium启动浏览器时,默认是打开一个新用户,不会加载原有的配置以及插件.但有些时候我们可能需要加载默认配置. 一.Chrome浏览器 1.在Chrome浏览器的地址栏输入:chrome://v ...

  3. 【数学+枚举】OpenJ_POJ - C17J Pairs

    https://vjudge.net/contest/171652#problem/J [题意] 问有多少个正整数对(x,y),使得存在正整数p,q满足 1 <= T <= 15 1 &l ...

  4. hdu 1824 2-sat问题(判断)

    /* 题意:u,v,w队长,队员,队长留下两个队员可以回家,两个队员留下,队长回家 2-sat问题,把两个队员看成一个整体就变成一个简单2-sat问题了 */ #include<stdio.h& ...

  5. msp430项目编程23

    msp430中项目---风扇控制系统 1.比较器工作原理 2.电路原理说明 3.代码(显示部分) 4.代码(功能实现) 5.项目总结 msp430项目编程 msp430入门学习

  6. 50个必备的实用jQuery代码段(转载)

    本文会给你们展示50个jquery代码片段,这些代码能够给你的javascript项目提供帮助.其中的一些代码段是从jQuery1.4.2才开始支持的做法,另一些则是真正有用的函数或方法,他们能够帮助 ...

  7. POJ 3013 【需要一点点思维...】【乘法分配率】

    题意: (这题明显感觉自己是英语渣) 给n个点从1到n标号,下面一行是每个点的权,另外给出m条边,下面是每条边的信息,两个端点+权值,边是无向边.你的任务是选出一些边,使这个图变成一棵树.这棵树的花费 ...

  8. 代码svn下载到本地后,关于数据库问题

    代码svn下载到本地后,关于数据库问题 1.那我本地还用搭建相应的数据库么?答案:当然不用啦,本地系统里已经配置好了数据库的网络地址了,端口号,密码啥的.即使你代码运行在本地,依然可以将数据传输到服务 ...

  9. Go---设计模式(策略模式)

    策略模式定义了算法家族,在调用算法家族的时候不感知算法的变化,客户也不会受到影响. 下面用<大话设计模式>中的一个实例进行改写. 例:超市中经常进行促销活动,促销活动的促销方法就是一个个策 ...

  10. SeaGlass:手工搭建伪基站监控系统

    “伪基站”即假基站,设备一般由主机和笔记本电脑或手机组成,通过短信群发器.短信发信机等相关设备能够搜取以其为中心.一定半径范围内的手机卡信息,利用2G移动通信的缺陷,通过伪装成运营商的基站,冒用他人手 ...