include/linux/init.h
/* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
* the device init subsection.
*
* The `id' arg to __define_initcall() is needed so that multiple initcalls
* can point at the same handler without causing duplicate-symbol build errors.
*/
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
/*
* A "pure" initcall has no dependencies on anything else, and purely
* initializes variables that couldn't be statically initialized.
*
* This only exists for built-in code, not for modules.
*/
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
#define __initcall(fn) device_initcall(fn)
/**
* module_init() - driver initialization entry point
* @x: function to be run at kernel boot time or module insertion
*
* module_init() will either be called during do_initcalls() (if
* builtin) or at module insertion time (if a module). There can only
* be one per module.
*/
/include/linux/init.h
#define module_init(x) __initcall(x);
可以发现这些*_initcall(fn)最终都是通过__define_initcall(level,fn)宏定义生成的。//这个版本少了id项
__define_initcall宏定义如下:
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
/include/linux/init.h
typedef int (*initcall_t)(void);
initcall_t是个函数指针
__initcall_##fn##id = fn;
__initcall_##fn##id是放在__section__(".initcall" level ".init"))段里的。
这句话的意思为定义一个 int (*initcall_t)(void)型的函数指针,函数存放在.initcall”level”.init section内。.initcall”level”.init section定义在vmlinux.lds内。
/* arch/arm/kernel/vmlinux.lds */
__initcall_start = .;
*(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init)
*(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init)
*(.initcall2.init) *(.initcall2s.init) *(.initcall3.init)
*(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init)
*(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init)
*(.initcall6.init) *(.initcall6s.init) *(.initcall7.init)
*(.initcall7s.init)
__initcall_end = .;
正好包括了上面init.h里定义的从
core_initcall到late_initcall等7个level等级的.initcall”level”.init section.
因此通过不同的*_initcall声明的函数指针最终都会存放不同level等级的.initcall”level”.init
section内。这些不同level的section按level等级高低依次存放。
下面我们再来看看,内核是什么时候调用存储在.initcall”level”.init section内的函数的。
内核是通过do_initcalls函数循环调用执行initcall.init section内的函数的,流程如下:
main.c
start_kernel -> rest_init -> kernel_thread -> kernel_init -> do_basic_setup -> do_initcalls
/*
* We need to finalize in a non-__init function or else race conditions
* between the root thread and the init thread may cause start_kernel to
* be reaped by free_initmem before the root thread has proceeded to
* cpu_idle.
*
* gcc-3.4 accidentally inlines this function, so use noinline.
*/
init/main.c
static void noinline __init_refok rest_init(void)
__releases(kernel_lock)
{
int pid;
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
unlock_kernel();
/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
init_idle_bootup_task(current);
preempt_enable_no_resched();
schedule();
preempt_disable();
/* Call into cpu_idle with preempt disabled */
cpu_idle();
}
/*
* Ok, the machine is now initialized. None of the devices
* have been touched yet, but the CPU subsystem is up and
* running, and memory and process management works.
*
* Now we can finally start doing some real work..
*/
init/main.c
static void __init do_basic_setup(void)
{
rcu_init_sched(); /* needed by module_init stage. */
init_workqueues();
usermodehelper_init();
driver_init();
init_irq_proc();
do_initcalls();
}
////////////////////////////
init/main.c
extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
static void __init do_initcalls(void)
{
initcall_t *call;
//批量执行initcallx.init段中的函数指针 (arch/arm/kernel/vmlinux.lds链接脚本)
//执行了所有驱动的入口函数
for (call = __early_initcall_end; call < __initcall_end; call++)
do_one_initcall(*call); //回调函数
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
阅读(1771) | 评论(0) | 转发(0) |