转自: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 }

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

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

  1. 神秘代理-Proxy

    前言: 代理模式作为常见的设计模式之一,在项目开发中不可或缺.本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正! 1.如何实现代理: [假设有个关于汽车移动(move)的计时需求]设计:Movea ...

  2. 深入理解javascript对象系列第三篇——神秘的属性描述符

    × 目录 [1]类型 [2]方法 [3]详述[4]状态 前面的话 对于操作系统中的文件,我们可以驾轻就熟将其设置为只读.隐藏.系统文件或普通文件.于对象来说,属性描述符提供类似的功能,用来描述对象的值 ...

  3. [BZOJ4408][Fjoi 2016]神秘数

    [BZOJ4408][Fjoi 2016]神秘数 试题描述 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1+13 = 1 ...

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

    在c++这片神秘的大陆上,有一个无往而不利的地下王国,据说其手段血腥残忍,却深得民心,因为,他们是侠,是剑胆琴心,诗肠酒骨的侠客,他们不知解决了多少疑难杂症,除去了多少问题漏洞,而他们的首领-> ...

  5. 揭开GrowingIO无埋点的神秘面纱

    揭开GrowingIO无埋点的神秘面纱   早在研究用户行为分析的时候,就发现国内的GrowingIO在宣传无埋点技术,最近正好抽出时间来研究一下所谓的无埋点到底是什么样的. 我分六部分来分析一下无埋 ...

  6. [bzoj4408][Fjoi2016]神秘数

    Description 一个可重复数字集合$S$的神秘数定义为最小的不能被$S$的子集的和表示的正整数. 例如$S={1,1,1,4,13}$, $1=1$, $2=1+1$, $3=1+1+1$, ...

  7. 揭开Sass和Compass的神秘面纱

    揭开Sass和Compass的神秘面纱 可能之前你像我一样,对Sass和Compass毫无所知,好一点儿的可能知道它们是用来作为CSS预处理的.那么,今天请跟我一起学习下Sass和Compass的一些 ...

  8. Java实现批量下载《神秘的程序员》漫画

    上周看了西乔的博客“西乔的九卦”.<神秘的程序员们>系列漫画感觉很喜欢,很搞笑.这些漫画经常出现在CSDN“程序员”杂志末页的,以前也看过一些. 后来就想下载下来,但是一张一张的点击右键“ ...

  9. ASP.NET 运行时详解 揭开请求过程神秘面纱

    对于ASP.NET开发,排在前五的话题离不开请求生命周期.像什么Cache.身份认证.Role管理.Routing映射,微软到底在请求过程中干了哪些隐秘的事,现在是时候揭晓了.抛开乌云见晴天,接下来就 ...

随机推荐

  1. WPF通过NPIO读写Excel操作

    自已摸索实现了对excel简单的上传和下载并做了一个封装类,下面分享一下. 先去官网:http://npoi.codeplex.com/下载需要引入dll(可以选择.net2.0或者.net4.0的d ...

  2. [转载]看看大牛们是怎样获得英文综述(reviews)的?

    对于做实验的童鞋来说,平时看综述必不可少,可是如何获得自己想要的却并不是一件容易的事情,这里整理并推荐几种行之有效的方法: 1. http://www.annualreviews.org/,这是一个顶 ...

  3. Dalvik虚拟机中DexClassLookup结构解析

    http://blog.csdn.net/roland_sun/article/details/46877563 原文如下: 在Android系统中,所有的类定义以及具体的代码都是包含在DEX文件中的 ...

  4. Dynamic len(set(a[L:R])) UVA - 12345(这么过分一定要写博客)

    给出一个有n个元素的数组,有以下两种操作:Q x y,求出区间[x,y)内不同元素的个数, M x y,把第x个元素的值修改为y.注意题目中的下标是从0开始的 这题超级超级坑 妈的一个水题找了几个小时 ...

  5. git与svn与github与码云的区别

    1.git与github(https://www.oschina.net/)的区别 Git(https://git-scm.com/)是一个版本控制工具 github是一个用git做版本控制的项目托管 ...

  6. [解决] User [dr.who] is not authorized to view the logs for application

    在hadoop集群启用权限控制后,发现job运行日志的ui访问不了, User [dr.who] is not authorized to view the logs for application ...

  7. nginx 静态服务器设置

    一.配置Nginx [root@VM_16_15_centos nginx]# cd conf.d/ [root@VM_16_15_centos conf.d]# ll total 4 -rwxr-x ...

  8. leetcode 刷题日志 2018-3-28

    树: 404. 左叶子之和 求所有左叶子结点之和 . 递归法 分析:递归法遍历结点,找左叶子结点 空指针判断 有左子节点?是叶子结点?是的话更新value的值 int sumOfLeftLeaves( ...

  9. 单词转换成向量形式 word2vec

    word2vec(word to vector)是一个将单词转换成向量形式的工具.可以把对文本内容的处理简化为向量空间中的向量运算,计算出向量空间上的相似度,来表示文本语义上的相 似度.word2ve ...

  10. Centos下Mysql密码忘记解决办法

    1.修改MySQL的登录设置: # vim /etc/my.cnf 在[mysqld]的段中加上一句:skip-grant-tables 例如: [mysqld] datadir=/var/lib/m ...