今天阅读启动相关代码(bootinfo.c), 看到了一个函数:
232 core_initcall(bootinfo_init);
不由想起前两天看到的: 607 subsys_initcall(gpiolib_debugfs_init)
结合平时我们使用的:4549 module_init(pxa3xx_nand_init);
(built-in时对应:249 #define module_init(x) __initcall(x);)
虽然大体知道他们的意思以及运行顺序,但还是打算稍微深入了解一下。于是开始跟踪代码。
还记得启动过程会调用一个函数:do_initcalls 以处理 initcalls, 于是以此为出发点,先看一下该函数本身:
664 static void __init do_initcalls(void)
665 {
666 initcall_t *call;
667 int count = preempt_count();
668
669 for (call = __initcall_start; call < __initcall_end; call++) {
673 unsigned long j, j0 = 0;
674 char *msg = NULL;
675 char msgbuf[40];
676 int result;
677
678 if (initcall_debug) {
686 j0 = jiffies;
687 }
688
689 result = (*call)();
690
691 if (initcall_debug) {
692 j = jiffies - j0;
705 if (j > 1) {
706 printk("initcall 0x%p ran for %d msecs: ",
707 *call, jiffies_to_msecs(j));
708 print_fn_descriptor_symbol("%s()/n",
709 (unsigned long) *call);
710 }
711 }
712
713 if (result && result != -ENODEV && initcall_debug) {
714 sprintf(msgbuf, "error code %d", result);
715 msg = msgbuf;
716 }
717 if (preempt_count() != count) {
718 msg = "preemption imbalance";
719 preempt_count() = count;
720 }
721 if (irqs_disabled()) {
722 msg = "disabled interrupts";
723 local_irq_enable();
724 }
725 if (msg) {
726 printk(KERN_WARNING "initcall at 0x%p", *call);
727 print_fn_descriptor_symbol(": %s()",
728 (unsigned long) *call);
729 printk(": returned with %s/n", msg);
730 }
731 }
732
733 /* Make sure there is no pending stuff from the initcall sequence */
734 flush_scheduled_work();
735 }
可以看到, 该函数其实就是按照一个初始函数列表去调用一个一个的函数。
一个有价值的信息时我们可以通过启动参数 initcall_debug=1 来让内核打印出这些函数的相关(时间、顺序等), 这对启动优化特别有帮助。
再看看do_initcalls 是何时被调用的:
744 static void __init do_basic_setup(void)
745 {
746 /* drivers will send hotplug events */
747 init_workqueues();
748 usermodehelper_init();
749 driver_init();
750 init_irq_proc();
751 do_initcalls();
752 }
821 static int __init kernel_init(void * unused)
822 {
823 lock_kernel();
824 /*
825 * init can run on any cpu.
826 */
827 set_cpus_allowed(current, CPU_MASK_ALL);
828 /*
829 * Tell the world that we're going to be the grim
830 * reaper of innocent orphaned children.
831 *
832 * We don't want people to have to make incorrect
833 * assumptions about where in the task array this
834 * can be found.
835 */
836 init_pid_ns.child_reaper = current;
837
838 cad_pid = task_pid(current);
839
840 smp_prepare_cpus(setup_max_cpus);
841
842 do_pre_smp_initcalls();
843
844 smp_init();
845 sched_init_smp();
846
847 cpuset_init_smp();
848
849 do_basic_setup();
850
851 /*
852 * check if there is an early userspace init. If yes, let it do all
853 * the work
854 */
855
856 if (!ramdisk_execute_command)
857 ramdisk_execute_command = "/init";
858
859 if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
860 ramdisk_execute_command = NULL;
861 prepare_namespace();
862 }
863
864 /*
865 * Ok, we have completed the initial bootup, and
866 * we're essentially up and running. Get rid of the
867 * initmem segments and start the user-mode stuff..
868 */
869 init_post();
870 return 0;
871 }
kernel_init 乃是 1号进程 init 的执行函数,可见do_initcalls是在系统运行到 init 进程后在即将进入用户空间前调用的。
再看看 初始函数列表 __initcall_start 是如何生成的, 搜索一下, 发现: 链接文件:arch/arm/kernel/vmlinux.lds.S 中有:
51 __initcall_start = .;
52 INITCALLS
53 __initcall_end = .;
其中:INITCALLS 定义如下:
328 #define INITCALLS /
329 *(.initcall0.init) /
330 *(.initcall0s.init) /
331 *(.initcall1.init) /
332 *(.initcall1s.init) /
333 *(.initcall2.init) /
334 *(.initcall2s.init) /
335 *(.initcall3.init) /
336 *(.initcall3s.init) /
337 *(.initcall4.init) /
338 *(.initcall4s.init) /
339 *(.initcall5.init) /
340 *(.initcall5s.init) /
341 *(.initcallrootfs.init) /
342 *(.initcall6.init) /
343 *(.initcall6s.init) /
344 *(.initcall7.init) /
345 *(.initcall7s.init)
原来在这些段里面定义的函数会被 do_initcalls 调用。由于这些段名似乎是合成的, 我们直接 freetext 查找可能不行,我们再从前面说到的 XXX_initcall 宏的定义那边去跟踪,希望能够衔接到一起。
我们看一下各种 XXX_initcall 宏的定义:
176 #define pure_initcall(fn) __define_initcall("",fn,0)
177
178 #define core_initcall(fn) __define_initcall("1",fn,1)
179 #define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
180 #define postcore_initcall(fn) __define_initcall("2",fn,2)
181 #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
182 #define arch_initcall(fn) __define_initcall("3",fn,3)
183 #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
184 #define subsys_initcall(fn) __define_initcall("4",fn,4)
185 #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
186 #define fs_initcall(fn) __define_initcall("5",fn,5)
187 #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
188 #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
189 #define device_initcall(fn) __define_initcall("6",fn,6)
190 #define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
191 #define late_initcall(fn) __define_initcall("7",fn,7)
192 #define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
193
194 #define __initcall(fn) device_initcall(fn)
再看一下 __define_initcall 的定义:
166 #define __define_initcall(level,fn,id) /
167 static initcall_t __initcall_##fn##id __used /
168 __attribute__((__section__(".initcall" level ".init"))) = fn
这样, 两边就衔接起来了: 原来所有的 XXX_initcall 宏所定义的函数都会放到同一个初始化函数列表中,只是不同的宏定义的函数会被放到不同的位置,数字小的会被先调用。
对于普通驱动,我们大多会使用 __initcall 或者 device_initcall宏, 那么使用同样宏的函数的调用顺序如何呢? 这取决与他们的链接顺序,我们可以到 System.map 文件去查看链接后的结果,比如, 可以看到:
__initcall_pxafb_init6 排在 __initcall_tty_init6 前面。
由于编译顺序在改变了文件名、目录名甚至配置文件后都可能变,所以我们应该尽量不要在同类的XXX_initcall 宏之间产生依赖关系。 实在需要,可以考虑使用 _sync 版本。