神秘的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 }
---------------------------------------------------------------------
神秘的subsys_initcall【转】的更多相关文章
- 神秘代理-Proxy
前言: 代理模式作为常见的设计模式之一,在项目开发中不可或缺.本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正! 1.如何实现代理: [假设有个关于汽车移动(move)的计时需求]设计:Movea ...
- 深入理解javascript对象系列第三篇——神秘的属性描述符
× 目录 [1]类型 [2]方法 [3]详述[4]状态 前面的话 对于操作系统中的文件,我们可以驾轻就熟将其设置为只读.隐藏.系统文件或普通文件.于对象来说,属性描述符提供类似的功能,用来描述对象的值 ...
- [BZOJ4408][Fjoi 2016]神秘数
[BZOJ4408][Fjoi 2016]神秘数 试题描述 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1+13 = 1 ...
- 在c++这片神秘的大陆上
在c++这片神秘的大陆上,有一个无往而不利的地下王国,据说其手段血腥残忍,却深得民心,因为,他们是侠,是剑胆琴心,诗肠酒骨的侠客,他们不知解决了多少疑难杂症,除去了多少问题漏洞,而他们的首领-> ...
- 揭开GrowingIO无埋点的神秘面纱
揭开GrowingIO无埋点的神秘面纱 早在研究用户行为分析的时候,就发现国内的GrowingIO在宣传无埋点技术,最近正好抽出时间来研究一下所谓的无埋点到底是什么样的. 我分六部分来分析一下无埋 ...
- [bzoj4408][Fjoi2016]神秘数
Description 一个可重复数字集合$S$的神秘数定义为最小的不能被$S$的子集的和表示的正整数. 例如$S={1,1,1,4,13}$, $1=1$, $2=1+1$, $3=1+1+1$, ...
- 揭开Sass和Compass的神秘面纱
揭开Sass和Compass的神秘面纱 可能之前你像我一样,对Sass和Compass毫无所知,好一点儿的可能知道它们是用来作为CSS预处理的.那么,今天请跟我一起学习下Sass和Compass的一些 ...
- Java实现批量下载《神秘的程序员》漫画
上周看了西乔的博客“西乔的九卦”.<神秘的程序员们>系列漫画感觉很喜欢,很搞笑.这些漫画经常出现在CSDN“程序员”杂志末页的,以前也看过一些. 后来就想下载下来,但是一张一张的点击右键“ ...
- ASP.NET 运行时详解 揭开请求过程神秘面纱
对于ASP.NET开发,排在前五的话题离不开请求生命周期.像什么Cache.身份认证.Role管理.Routing映射,微软到底在请求过程中干了哪些隐秘的事,现在是时候揭晓了.抛开乌云见晴天,接下来就 ...
随机推荐
- request 域 个人理解
1.转发到另一个servlet时候 地址还是输入当前的servlet 2.通过服务器转到另一个servlet时候 另一个servlet是最终接收端 端到端模式 相当于这个东西是发给自己的 只不过经过多 ...
- 【bzoj5064】B-number 数位dp
题目描述 B数的定义:能被13整除且本身包含字符串"13"的数. 例如:130和2613是B数,但是143和2639不是B数. 你的任务是计算1到n之间有多少个数是B数. 输入 输 ...
- BZOJ4820 SDOI2017硬币游戏(概率期望+高斯消元+kmp)
容易想到的做法是建出AC自动机,高斯消元.然而自动机上节点数量是nm的. 注意到我们要求的变量只有n个,考虑将其他不用求的节点合并为一个变量.这个变量即表示随机生成一个串,其不包含任何一个模板串的概率 ...
- zoj 1298 Domino Effect (最短路径)
Domino Effect Time Limit: 2 Seconds Memory Limit: 65536 KB Did you know that you can use domino ...
- 【题解】HNOI2014世界树
脑子不清醒的时候千万别写题.写题写不下去了千万别死扛,重构才是你唯一的出路QAQ 昨天很想快点写道题,思路没有很清晰的时候就写了,结果……今天一怒之下决定重整思路重构代码,其实不过是半个小时的事情…… ...
- [学习笔记]NTT——快速数论变换
先要学会FFT[学习笔记]FFT——快速傅里叶变换 一.简介 FFT会爆精度.而且浮点数相乘常数比取模还大. 然后NTT横空出世了 虽然单位根是个好东西.但是,我们还有更好的东西 我们先选择一个模数, ...
- $.ajax() 方法的理解
jquery中的ajax方法理解,AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术.AJAX ...
- 动态规划:LIS
题目中的严格二字,表示的意思是不允许≥或者是≤的情况出现,只允许>的情况以及<的情况 经典问题是NOIP合唱队形,在这个题目中,既求了最长上升子序列,也求了最长下降子序列 其最终的结果由两 ...
- [洛谷P2596] [ZJOI2006]书架
洛谷题目链接:书架 题目描述 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书,看完后放回书柜然后 ...
- [Luogu 3958] NOIP2017 D2T1 奶酪
题目链接 人生第一篇题解,多多关照吧. 注意事项: 1.多组数据,每次要先初始化. 2.因为涉及到开根,所以记得开double. 整体思路: 建图,判断「起点」与「终点」是否连通. 方法可选择搜索(我 ...