分类: LINUX
2014-10-02 18:28:42
原文地址:神秘的subsys_initcall 作者:tq08g2z
在内核代码里到处都能看到这个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
}
---------------------------------------------------------------------