一、vmlinux.lds简介
从arch/arm/kernel/vmlinux.lds分析Linux内核第一行启动代码。找到
ENTRY(stext)
入口函数是stext,image和zImage是经过压缩的,Linux内核会先进行解压缩,解压缩完成以后就要运行Linux内核。要求:
1、MMU关闭
2、D cache关闭
3、I cache无所谓
4、r0 = 0。
5、r1 = machine nr
6、r2=atags 或设备树
二、Linux入口stext
__vet_atags 函数验证atags或dtb是否有效,如果使用设备树的话就是dtb。
__create_page_tables 创建页表。
ldr r13, =__mmap_switched 也就是r13保存__mmap_switched。
__enable_mmu 使能MMU-> __turn_mmu_on->_mmap_switched->start_kernel 启动内核。
Linux内核第一阶段。
三、__mmap_switched函数
四、start_kernel函数
Linux内核第二阶段
/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga/init/main.c
asmlinkage __visible void __init start_kernel(void)
{char *command_line;char *after_dashes;/** Need to run as early as possible, to initialize the* lockdep hash:*/lockdep_init();set_task_stack_end_magic(&init_task);smp_setup_processor_id();debug_objects_early_init();/** Set up the the initial canary ASAP:*/boot_init_stack_canary();cgroup_init_early();local_irq_disable();early_boot_irqs_disabled = true;/** Interrupts are still disabled. Do necessary setups, then* enable them*/boot_cpu_init();page_address_init();pr_notice("%s", linux_banner);setup_arch(&command_line);mm_init_cpumask(&init_mm);setup_command_line(command_line);setup_nr_cpu_ids();setup_per_cpu_areas();smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */build_all_zonelists(NULL, NULL);page_alloc_init();pr_notice("Kernel command line: %s\n", boot_command_line);parse_early_param();after_dashes = parse_args("Booting kernel",static_command_line, __start___param,__stop___param - __start___param,-1, -1, &unknown_bootoption);if (!IS_ERR_OR_NULL(after_dashes))parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,set_init_arg);jump_label_init();/** These use large bootmem allocations and must precede* kmem_cache_init()*/setup_log_buf(0);pidhash_init();vfs_caches_init_early();sort_main_extable();trap_init();mm_init();/** Set up the scheduler prior starting any interrupts (such as the* timer interrupt). Full topology setup happens at smp_init()* time - but meanwhile we still have a functioning scheduler.*/sched_init();/** Disable preemption - early bootup scheduling is extremely* fragile until we cpu_idle() for the first time.*/preempt_disable();if (WARN(!irqs_disabled(),"Interrupts were enabled *very* early, fixing it\n"))local_irq_disable();idr_init_cache();rcu_init();/* trace_printk() and trace points may be used after this */trace_init();context_tracking_init();radix_tree_init();/* init some links before init_ISA_irqs() */early_irq_init();init_IRQ();tick_init();rcu_init_nohz();init_timers();hrtimers_init();softirq_init();timekeeping_init();time_init();sched_clock_postinit();perf_event_init();profile_init();call_function_init();WARN(!irqs_disabled(), "Interrupts were enabled early\n");early_boot_irqs_disabled = false;local_irq_enable();kmem_cache_init_late();/** HACK ALERT! This is early. We're enabling the console before* we've done PCI setups etc, and console_init() must be aware of* this. But we do want output early, in case something goes wrong.*/console_init();if (panic_later)panic("Too many boot %s vars at `%s'", panic_later,panic_param);lockdep_info();/** Need to run this when irqs are enabled, because it wants* to self-test [hard/soft]-irqs on/off lock inversion bugs* too:*/locking_selftest();#ifdef CONFIG_BLK_DEV_INITRDif (initrd_start && !initrd_below_start_ok &&page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",page_to_pfn(virt_to_page((void *)initrd_start)),min_low_pfn);initrd_start = 0;}
#endifpage_ext_init();debug_objects_mem_init();kmemleak_init();setup_per_cpu_pageset();numa_policy_init();if (late_time_init)late_time_init();sched_clock_init();calibrate_delay();pidmap_init();anon_vma_init();acpi_early_init();
#ifdef CONFIG_X86if (efi_enabled(EFI_RUNTIME_SERVICES))efi_enter_virtual_mode();
#endif
#ifdef CONFIG_X86_ESPFIX64/* Should be run before the first non-init thread is created */init_espfix_bsp();
#endifthread_info_cache_init();cred_init();fork_init();proc_caches_init();buffer_init();key_init();security_init();dbg_late_init();vfs_caches_init(totalram_pages);signals_init();/* rootfs populating might need page-writeback */page_writeback_init();proc_root_init();nsfs_init();cpuset_init();cgroup_init();taskstats_init_early();delayacct_init();check_bugs();acpi_subsystem_init();sfi_init_late();if (efi_enabled(EFI_RUNTIME_SERVICES)) {efi_late_init();efi_free_boot_services();}ftrace_init();/* Do the rest non-__init'ed, we're now alive */rest_init();
}
start_kernel-> rest_init-> kernel_thread(kernel_init, NULL, CLONE_FS); 创建kernel_init进程。也就是
init进程,PID=1-> kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); 创建kthreadd
进程,进程PID为2。-> cpu_startup_entry 进入空闲进程,也就是主进程退化为空闲进程,idle。
启动开发板,输入:ps -A,列出当前系统所有进程。
五、reset_init函数
/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga/init/main.c
static noinline void __init_refok rest_init(void)
{int pid;rcu_scheduler_starting();smpboot_thread_init();/** We need to spawn init first so that it obtains pid 1, however* the init task will end up wanting to create kthreads, which, if* we schedule it before we create kthreadd, will OOPS.*/kernel_thread(kernel_init, NULL, CLONE_FS);numa_default_policy();pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);rcu_read_lock();kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);rcu_read_unlock();complete(&kthreadd_done);/** The boot idle thread must execute schedule()* at least once to get things moving:*/init_idle_bootup_task(current);schedule_preempt_disabled();/* Call into cpu_idle with preempt disabled */cpu_startup_entry(CPUHP_ONLINE);
}
六、init进程
kernel_init -> kernel_init_freeable-> 设置标准输入、标准输出、标准错误使用console,比如ttymxc0-> ramdisk_execute_command = "/init";-> 检查/init是否存在,存在的话就运行。-> uboot传递给Linux内核的bootargs可以自定init=xxx,或者叫命令行参数。
有一些开发板会设置init=linuxrc。-> 试着运行/sbin/init。-> /etc/init-> /bin/init-> /bin/sh
可以看出,最终引出根文件系统。