神秘的subsys_initcall【转】

转自:http://blog.chinaunix.net/uid-12567959-id-161015.html

在内核代码里到处都能看到这个subsys_initcall(),而它到底是干什么的呢?让我们来揭开它的神秘面纱。

先来看一段代码:

---------------------------------------------------------------------

include/linux/init.h

174 /*

175  * Early initcalls run before initializing SMP.

176  *

177  * Only for built-in code, not modules.

178  */

179 #define early_initcall(fn)    __define_initcall("early",fn,early)

180

181 /*

182  * A "pure" initcall has no dependencies on anything else, and purely

183  * initializes variables that couldn‘t be statically initialized.

184  *

185  * This only exists for built-in code, not for modules.

186  */

187 #define pure_initcall(fn)              __define_initcall("",fn,0)

188

189 #define core_initcall(fn)             __define_initcall("1",fn,1)

190 #define core_initcall_sync(fn)      __define_initcall("1s",fn,1s)

191 #define postcore_initcall(fn)         __define_initcall("2",fn,2)

192 #define postcore_initcall_sync(fn)  __define_initcall("2s",fn,2s)

193 #define arch_initcall(fn)             __define_initcall("3",fn,3)

194 #define arch_initcall_sync(fn)      __define_initcall("3s",fn,3s)

195 #define subsys_initcall(fn)           __define_initcall("4",fn,4)

196 #define subsys_initcall_sync(fn)    __define_initcall("4s",fn,4s)

197 #define fs_initcall(fn)               __define_initcall("5",fn,5)

198 #define fs_initcall_sync(fn)        __define_initcall("5s",fn,5s)

199 #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)

200 #define device_initcall(fn)           __define_initcall("6",fn,6)

201 #define device_initcall_sync(fn)    __define_initcall("6s",fn,6s)

202 #define late_initcall(fn)             __define_initcall("7",fn,7)

203 #define late_initcall_sync(fn)      __define_initcall("7s",fn,7s)

---------------------------------------------------------------------

类似于subsys_initcall()还有很多,但它们都依赖于__define_initcall(),再来看__define_initcall()的定义:

---------------------------------------------------------------------

include/linux/init.h

131 typedef int (*initcall_t)(void);

165  *

166  * The `id‘ arg to __define_initcall() is needed so that multiple

167  * initcalls can point at the same handler without causing duplicate-symbol build errors.

168  */

169

170 #define __define_initcall(level,fn,id) \

171       static initcall_t __initcall_##fn##id __used \

172       __attribute__((__section__(".initcall" level ".init"))) = fn

173

---------------------------------------------------------------------

__define_initcall()宏只是定义一个initcall_t类型的静态变量,并且声明要把这个静态变量放在特定的段里而已。上面我们看到initcall_t即是指向一个无参数有int返回值的函数的指针。

许多的子系统都有自己的初始化函数,而这些初始化的函数又根据功能不同被分开在不同的子段里,子段的排列顺序则由链接决定。为了向后兼容,initcall()把调用,也就是一个个指向初始化函数的函数指针放进设备初始化子段里。

在各个平台的链接脚本文件arch/xxx/kernel/vmlinux.lds.S中,我们总能看到下面的语句:

INIT_CALLS

这个宏有如下的定义:

---------------------------------------------------------------------

include/asm-generic/vmlinux.lds.h

606 #define INIT_CALLS                                           \

607                 VMLINUX_SYMBOL(__initcall_start) = .;        \

608                 INITCALLS                                    \

609                 VMLINUX_SYMBOL(__initcall_end) = .;

---------------------------------------------------------------------

INIT_CALLS即是定义一个新的段,而定义段的字段的任务则由宏INITCALLS完成:

---------------------------------------------------------------------

include/asm-generic/vmlinux.lds.h

585 #define INITCALLS                                            \

586         *(.initcallearly.init)                               \

587         VMLINUX_SYMBOL(__early_initcall_end) = .;            \

588         *(.initcall0.init)                                   \

589         *(.initcall0s.init)                                  \

590         *(.initcall1.init)                                   \

591         *(.initcall1s.init)                                  \

592         *(.initcall2.init)                                   \

593         *(.initcall2s.init)                                  \

594         *(.initcall3.init)                                   \

595         *(.initcall3s.init)                                  \

596         *(.initcall4.init)                                   \

597         *(.initcall4s.init)                                  \

598         *(.initcall5.init)                                   \

599         *(.initcall5s.init)                                  \

600         *(.initcallrootfs.init)                              \

601         *(.initcall6.init)                                   \

602         *(.initcall6s.init)                                  \

603         *(.initcall7.init)                                   \

604         *(.initcall7s.init)

---------------------------------------------------------------------

而这些初始化函数又是在何时调用的呢?我们看到start_kernel()-> rest_init()-> kernel_init()-> do_basic_setup(void)-> do_initcalls(),而正是do_initcalls()处理了这些初始化函数,其定义为:

---------------------------------------------------------------------

init/main.c

765 extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];

766

767 static void __init do_initcalls(void)

768 {

769         initcall_t *fn;

770

771         for (fn = __early_initcall_end; fn < __initcall_end; fn++)

772                 do_one_initcall(*fn);

773

774         /* Make sure there is no pending stuff from the initcall sequence */

775         flush_scheduled_work();

776 }

---------------------------------------------------------------------

do_initcalls()又调用do_one_initcall()函数类处理这些调用。

---------------------------------------------------------------------

init/main.c

715 static char msgbuf[64];

716 static struct boot_trace_call call;

717 static struct boot_trace_ret ret;

718

719 int do_one_initcall(initcall_t fn)

720 {

721      int count = preempt_count();

722      ktime_t calltime, delta, rettime;

723

724      if (initcall_debug) {

725         call.caller = task_pid_nr(current);

726         printk("calling  %pF @ %i\n", fn, call.caller);

727         calltime = ktime_get();

728         trace_boot_call(&call, fn);

729         enable_boot_trace();

730     }

731

732     ret.result = fn();

733

734     if (initcall_debug) {

735        disable_boot_trace();

736        rettime = ktime_get();

737        delta = ktime_sub(rettime, calltime);

738        ret.duration = (unsigned long long) ktime_to_ns(delta) >> 10;

739        trace_boot_ret(&ret, fn);

740        printk("initcall %pF returned %d after %Ld usecs\n", fn,

741        ret.result, ret.duration);

742     }

743

744     msgbuf[0] = 0;

745

746     if (ret.result && ret.result != -ENODEV && initcall_debug)

747        sprintf(msgbuf, "error code %d ", ret.result);

748

749     if (preempt_count() != count) {

750        strlcat(msgbuf, "preemption imbalance ", sizeof(msgbuf));

751        preempt_count() = count;

752     }

753     if (irqs_disabled()) {

754        strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));

755        local_irq_enable();

756     }

757     if (msgbuf[0]) {

758        printk("initcall %pF returned with %s\n", fn, msgbuf);

759     }

760

761     return ret.result;

762 }

---------------------------------------------------------------------

时间: 2024-10-20 00:07:19

神秘的subsys_initcall【转】的相关文章

【BZOJ-4408】神秘数 可持久化线段树

4408: [Fjoi 2016]神秘数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 475  Solved: 287[Submit][Status][Discuss] Description 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13}, 1 = 1 2 = 1+1 3 = 1+1+1 4 = 4 5 = 4+1 6 = 4+1+1 7 = 4+1+1+1 8无法表示为集合S的子集的

在c++这片神秘的大陆上

在c++这片神秘的大陆上,有一个无往而不利的地下王国,据说其手段血腥残忍,却深得民心,因为,他们是侠,是剑胆琴心,诗肠酒骨的侠客,他们不知解决了多少疑难杂症,除去了多少问题漏洞,而他们的首领->类,则是一个更为神秘的存在,传说其有九千张面孔,其手下赫赫有名的分别是:构造,析构,静态, 常,普通,当然还有一把绝世神兵this,相当于召唤门,除了静态没有,其他成员都有 ,以及其侠客团->对象. 静态就像一个妖娆的美女 , 看似静如月 ,实则动如烈火,心思玲珑,变幻莫测,潜 伏类的身侧 ,对外她是直

工信部曝光周鸿祎四款神秘新机

周鸿祎的神秘新机终于在新浪微博首露真容,不过这到底是未来的奇酷手机还是酷派新品并未得到证实.近日,工信部同时出现四款酷派神秘产品的入网信息,其中有一款采用联发科热门的MT6753八核芯片,和魅蓝Note2配置相近,不过并没有像魅族那样省成本,而是直接一款手机就可支持支持全网通,从配置来看,这款应该是主打低端品牌. 奇酷手机官方微博曝光的新手机 但未确认身份 至于具体会对应哪款手机目前还不得而知,由于有奇酷手机的介入,大部分猜测都是即将发布的是360手机,然而背面的酷派logo则表明其很可能还是酷

subsys_initcall宏定义的一个例子,acpi/bus.c里面。

static int __init acpi_init(void) { int result; if (acpi_disabled) { printk(KERN_INFO PREFIX "Interpreter disabled.\n"); return -ENODEV; } acpi_kobj = kobject_create_and_add("acpi", firmware_kobj); if (!acpi_kobj) { printk(KERN_WARNING

EJB到底是什么,真的那么神秘吗??

1. 我们不禁要问,什么是"服务集群"?什么是"企业级开发"? 既然说了EJB 是为了"服务集群"和"企业级开发",那么,总得说说什么是所谓的"服务集群"和"企业级开发"吧!这个问题其实挺关键的,因为J2EE 中并没有说明白,也没有具体的指标或者事例告诉广大程序员什么时候用EJB 什么时候不用.于是大家都产生一些联想,认为EJB"分布式运算"指得是"负载均衡

揭开观察者设计模式的神秘面纱,手把手教你写监听器

我们在写代码的时候,遇到最常用的就是监听器了.那么实际中,我们也要进行事件的监听.而有些事件是业务逻辑需要实现的,跟随事物变化动态变化的.假如说我们要实现一个事件,有位置的监听,有颜色的监听,有坐标的监听,有速度的监听,那么这么多监听的事件.那么我们就需要这么多个监听器.这些监听器如何被管理呢.我们可以创造一个类似管理员身份的神秘角色,这个角色就是一个监听器池说一个监听器池,可以移除和增加监听器.当我们触发某一事件的时候,需要这些监听器全部执行监听. 现在我们来模拟一下按钮Button的实现.

经过多次强攻之后,赫柏带领的军团不仅没能击败鲁卡斯,反而被鲁卡斯打得七零八落,赫柏终于体会到了高阶天之驱逐者的强大实力。 不过,赫柏最终还是找到了鲁卡斯的致命弱点,他发现鲁卡斯喜欢收集上古卷轴,因为上古卷轴能够让鲁卡斯获得神秘之力。 卢卡斯决定使用上古卷轴将卢卡斯引诱到绝域之门,利用绝域之门的力量消灭卢卡斯。 赫柏注意到卢卡斯喜欢收集不同的卷轴,如果总是捡到相同的上古卷轴,它的兴趣就会逐渐降低。

经过多次强攻之后,赫柏带领的军团不仅没能击败鲁卡斯,反而被鲁卡斯打得七零八落,赫柏终于体会到了高阶天之驱逐者的强大实力.不过,赫柏最终还是找到了鲁卡斯的致命弱点,他发现鲁卡斯喜欢收集上古卷轴,因为上古卷轴能够让鲁卡斯获得神秘之力.卢卡斯决定使用上古卷轴将卢卡斯引诱到绝域之门,利用绝域之门的力量消灭卢卡斯.赫柏注意到卢卡斯喜欢收集不同的卷轴,如果总是捡到相同的上古卷轴,它的兴趣就会逐渐降低.赫柏现在拥有N种不同的卷轴,每种卷轴有Ai个.现在他要将这N个卷轴分散在鲁卡斯领地到绝域之门的路上,每一种排

了解黑客的关键工具---揭开Shellcode的神秘面纱

ref:  http://zhaisj.blog.51cto.com/219066/61428/ 对于初期接触网络安全的人来说,Shellcode是很神秘的东西,对于网络攻击过程中的嗅探信息.漏洞剖析都是可以理解的,但真正利用漏洞入侵时,通过把一段二进制码送入后并执行,就可以获得目标机器的控制权,之后的事情是属于爱好者学习技术,还是黑客的行为,就看攻击者的一念之差了.Shellcode就好象神秘的武器,安全防护变得如此不堪一击.Shellcode究竟是什么样的程序?是什么特殊代码?如何才能学会编

神秘的Flash Translation Layer (FTL)

如果仅仅是SSD的使用者,一定不会在意在SSD内部居然还存在一个复杂的软件层Flash Translation Layer(FTL).其实就是这个FTL才是SSD固态硬盘的软件核心技术.正因为有了FTL,NAND Flash才能被当成硬盘来使用:文件系统才可以直接把SSD当成普通块设备来使用.由于FTL是SSD设计厂商最为重要的核心技术,因此,没有一家厂商愿意透露这方面的技术信息,并且也一直没有业内的技术规范.标准存在. FTL的重要程度在于决定了一个SSD的使用寿命.性能和可靠性.一旦FTL出