分类:
2012-07-19 17:26:41
在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函数之间必须保证一定的调用顺序的问题。
在kernel中有很多__init,下面是其定义:
file:/include/linux/init.h
43 #define __init __attribute__ ((__section__ (".init.text"))) __cold
44 #define __initdata __attribute__ ((__section__ (".init.data")))
45 #define __exitdata __attribute__ ((__section__(".exit.data")))
46 #define __exit_call __attribute_used__ __attribute__ ((__section__ (".exitcall.exit")))
关于__attribute__ ((__section__ (".init.text"))) __cold的作用,参考 info gcc C Extensions Attribute Syntax
简单来说是告诉连接器把标记的数据或者函数放到指定段。
linux中把一些启动及初始化时候用的数据用__init标识,然后在适当的时候把它们释放,回收内存。因为初始化初始化一次后就没有用了,这部分内存可以释放了。
说到这个__init,就不能不说module_init,subsys_initcall。
在init.h中我们能够找到 #define subsys_initcall(fn) __define_initcall("4",fn,4)
file:/include/linux/init.h
110 #define __define_initcall(level,fn,id) \
111 static initcall_t __initcall_##fn##id __attribute_used__ \
112 __attribute__((__section__(".initcall" level ".init"))) = fn
subsys_initcall(usb_init)
转换后就变成了 static initcall_t __initcall_usbinit4 __attribute_used__ \
__attribute__((__section__(".initcall 4.init"))) = usb_init
就是把usb_init的函数入口指针存放在.initcall4.init中。
file:/include/asm-generic/vmlinux.lds.h
239 #define INITCALLS \
240 *(.initcall0.init) \
241 *(.initcall0s.init) \
242 *(.initcall1.init) \
243 *(.initcall1s.init) \
244 *(.initcall2.init) \
245 *(.initcall2s.init) \
246 *(.initcall3.init) \
247 *(.initcall3s.init) \
248 *(.initcall4.init) \
249 *(.initcall4s.init) \
250 *(.initcall5.init) \
251 *(.initcall5s.init) \
252 *(.initcallrootfs.init) \
253 *(.initcall6.init) \
254 *(.initcall6s.init) \
255 *(.initcall7.init) \
256 *(.initcall7s.init)
file:/arch/kernel/vmlinux_32.lds.S
144 .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {
145 __initcall_start = .;
146 INITCALLS
147 __initcall_end = .;
148 }
系统是如何执行这些函数呢?
进入start_kernel
start_kernel -->rest_init() -->kernel_init() --> do_basic_setup() -->do_initcalls()
这个do_initcalls()就是调用这些函数的地方。
file:/init/main.c
662 static void __init do_initcalls(void)
663 {
664 initcall_t *call;
665 int count = preempt_count();
666
667 for (call = __initcall_start; call < __initcall_end; call++) {
668 ktime_t t0, t1, delta;
669 char *msg = NULL;
670 char msgbuf[40];
671 int result;
672
673 if (initcall_debug) {
674 printk("Calling initcall 0x%p", *call);
675 print_fn_descriptor_symbol(": %s()",
676 (unsigned long) *call);
677 printk("\n");
678 t0 = ktime_get();
679 }
680
681 result = (*call)();
682
683 if (initcall_debug) {
684 t1 = ktime_get();
685 delta = ktime_sub(t1, t0);
686
687 printk("initcall 0x%p", *call);
688 print_fn_descriptor_symbol(": %s()",
689 (unsigned long) *call);
690 printk(" returned %d.\n", result);
691
692 printk("initcall 0x%p ran for %Ld msecs: ",
693 *call, (unsigned long long)delta.tv64 >> 20);
694 print_fn_descriptor_symbol("%s()\n",
695 (unsigned long) *call);
696 }
697
698 if (result && result != -ENODEV && initcall_debug) {
699 sprintf(msgbuf, "error code %d", result);
700 msg = msgbuf;
701 }
702 if (preempt_count() != count) {
703 msg = "preemption imbalance";
704 preempt_count() = count;
705 }
706 if (irqs_disabled()) {
707 msg = "disabled interrupts";
708 local_irq_enable();
709 }
710 if (msg) {
711 printk(KERN_WARNING "initcall at 0x%p", *call);
712 print_fn_descriptor_symbol(": %s()",
713 (unsigned long) *call);
714 printk(": returned with %s\n", msg);
715 }
716 }
717
718 /* Make sure there is no pending stuff from the initcall sequence */
719 flush_scheduled_work();
720 }