今天看了下IMX28驱动初始化部分时有些迷惑,跟踪了下代码,现把调用过程记录下来 内核初始化后跳转到start_kernel() ,start_kernel()最后调用rest_init();驱动中初始化内容在rest_init()中调用 init/main.c中 static noinline void __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); rcu_scheduler_starting(); preempt_enable_no_resched(); schedule(); preempt_disable(); /* Call into cpu_idle with preempt disabled */ cpu_idle(); } kernel_init在 static int __init kernel_init(void * unused) { lock_kernel(); /* * init can allocate pages on any node */ set_mems_allowed(node_possible_map); /* * init can run on any cpu. */ set_cpus_allowed_ptr(current, cpu_all_mask); /* * Tell the world that we're going to be the grim * reaper of innocent orphaned children. * * We don't want people to have to make incorrect * assumptions about where in the task array this * can be found. */ init_pid_ns.child_reaper = current; cad_pid = task_pid(current); smp_prepare_cpus(setup_max_cpus); do_pre_smp_initcalls(); start_boot_trace(); smp_init(); sched_init_smp(); do_basic_setup(); //在此基本设置 /* * check if there is an early userspace init. If yes, let it do all * the work */ if (!ramdisk_execute_command) ramdisk_execute_command = "/init"; if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; prepare_namespace(); } /* * Ok, we have completed the initial bootup, and * we're essentially up and running. Get rid of the * initmem segments and start the user-mode stuff.. */ init_post(); return 0; } do_basic_setup(); static void __init do_basic_setup(void) { rcu_init_sched(); /* needed by module_init stage. */ init_workqueues(); cpuset_init_smp(); usermodehelper_init(); driver_init();//驱动初始化 init_irq_proc(); do_ctors(); do_initcalls(); //调用 } 关于调用顺序知识 在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函数之间必须保证一定的调用顺序的问题。 ******************************************** MACHINE_START(MX28EVK, "Freescale MX28EVK board") .phys_io = 0x80000000, .io_pg_offst = ((0xf0000000) >> 18) & 0xfffc, .boot_params = 0x40000100, .fixup = fixup_board, .map_io = mx28_map_io, .init_irq = mx28_irq_init, .init_machine = mx28evk_init_machine, .timer = &mx28_timer.timer, MACHINE_END 由上发现,MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。 各个成员函数在不同时期被调用: 1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。 2. init_irq在start_kernel() --> init_IRQ() --> init_arch_irq() 被调用
3. map_io 在 setup_arch() --> paging_init() --> devicemaps_init()被调用 其他主要都在 setup_arch() 中用到。
************************************* static void __init do_initcalls(void) { initcall_t *call; 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(); } 这个__initcall_start是在文件 arch/xxx/kernel/vmlinux.lds.S (其中的xxx 是你的体系结构的名称,例如i386) |
|
来自: langhuayipian > 《linux内核》