一 内核启动
uboot -> start_kernel -> rest_init() -> kernel_thread(kernel_init)-->kernel_init_freeable()-->run_init_process ->
1,start_kernel 函数(trunk/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/Linux -ramips_rt305x/linux-3.10.36/init/main.c)
<pre name= "code" class = "cpp" >asmlinkage void __init start_kernel( void ) { char * command_line; extern const struct kernel_param __start___param[], __stop___param[]; lockdep_init(); smp_setup_processor_id(); debug_objects_early_init(); boot_init_stack_canary(); cgroup_init_early(); local_irq_disable(); early_boot_irqs_disabled = true ; boot_cpu_init(); page_address_init(); pr_notice("%s" , linux_banner); setup_arch(&command_line); mm_init_owner(&init_mm, &init_task); mm_init_cpumask(&init_mm); setup_command_line(command_line); setup_nr_cpu_ids(); setup_per_cpu_areas(); smp_prepare_boot_cpu(); build_all_zonelists(NULL, NULL); page_alloc_init(); pr_notice("Kernel command line: %s\n" , boot_command_line); parse_early_param(); parse_args("Booting kernel" , static_command_line, __start___param, __stop___param - __start___param, -1, -1, &unknown_bootoption); jump_label_init(); setup_log_buf(0); pidhash_init(); vfs_caches_init_early(); sort_main_extable(); trap_init(); mm_init(); sched_init(); preempt_disable(); if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n" )) local_irq_disable(); idr_init_cache(); perf_event_init(); rcu_init(); tick_nohz_init(); radix_tree_init(); early_irq_init(); init_IRQ(); tick_init(); init_timers(); hrtimers_init(); softirq_init(); timekeeping_init(); time_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(); console_init(); if (panic_later) panic(panic_later, panic_param); lockdep_info(); locking_selftest(); #ifdef CONFIG_BLK_DEV_INITRD if (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; } #endif page_cgroup_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(); #ifdef CONFIG_X86 if (efi_enabled(EFI_RUNTIME_SERVICES)) efi_enter_virtual_mode(); #endif thread_info_cache_init(); cred_init(); fork_init(totalram_pages); proc_caches_init(); buffer_init(); key_init(); security_init(); dbg_late_init(); vfs_caches_init(totalram_pages); signals_init(); page_writeback_init(); #ifdef CONFIG_PROC_FS proc_root_init(); #endif cgroup_init(); cpuset_init(); taskstats_init_early(); delayacct_init(); check_bugs(); acpi_early_init(); sfi_init_late(); if (efi_enabled(EFI_RUNTIME_SERVICES)) { efi_late_init(); efi_free_boot_services(); } ftrace_init(); rest_init(); }
在这里,启动应用程序
static int __ref kernel_init( void *unused) { kernel_init_freeable(); async_synchronize_full(); free_initmem(); mark_rodata_ro(); system_state = SYSTEM_RUNNING; numa_default_policy(); flush_delayed_fput(); if (ramdisk_execute_command) { if (!run_init_process(ramdisk_execute_command)) return 0; pr_err("Failed to execute %s\n" , ramdisk_execute_command); } if (execute_command) { if (!run_init_process(execute_command)) return 0; pr_err("Failed to execute %s. Attempting defaults...\n" , execute_command); } if (!run_init_process( "/etc/preinit" ) || !run_init_process("/sbin/init" ) || !run_init_process("/etc/init" ) || !run_init_process("/bin/init" ) || !run_init_process("/bin/sh" )) return 0; panic("No init found. Try passing init= option to kernel. " "See Linux Documentation/init.txt for guidance." ); }
static noinline void __init kernel_init_freeable( void ) { wait_for_completion(&kthreadd_done); gfp_allowed_mask = __GFP_BITS_MASK; set_mems_allowed(node_states[N_MEMORY]); set_cpus_allowed_ptr(current, cpu_all_mask); cad_pid = task_pid(current); smp_prepare_cpus(setup_max_cpus); do_pre_smp_initcalls(); lockup_detector_init(); smp_init(); sched_init_smp(); do_basic_setup(); if (sys_open(( const char __user *) "/dev/console" , O_RDWR, 0) < 0) pr_err("Warning: unable to open an initial console.\n" ); (void ) sys_dup(0); (void ) sys_dup(0); 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(); } load_default_modules(); }
启动第一个应用,init
trunk\build_dir\target-mipsel_24kec+dsp_uClibc-0.9.33.2\procd-2014-03-18\initd\init.c
int main(int argc, char **argv) { pid_t pid; sigaction(SIGTERM, &sa_shutdown, NULL); sigaction(SIGUSR1, &sa_shutdown, NULL); sigaction(SIGUSR2, &sa_shutdown, NULL); early(); cmdline(); watchdog_init(1); pid = fork(); if (!pid) { char *kmod[] = { "/sbin/kmodloader" , "/etc/modules-boot.d/" , NULL }; if (debug < 3) { int fd = open( "/dev/null" , O_RDWR); if (fd > -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (fd > STDERR_FILENO) close(fd); } } execvp(kmod[0], kmod); ERROR("Failed to start kmodloader\n" ); exit(-1); } if (pid <= 0) ERROR("Failed to start kmodloader instance\n" ); uloop_init(); preinit(); uloop_run(); return 0; }
trunk\build_dir\target-mipsel_24kec+dsp_uClibc-0.9.33.2\procd-2014-03-18\state.c
static void state_enter( void ) { char ubus_cmd[] = "/sbin/ubusd" ; switch (state) { case STATE_EARLY: LOG("- early -\n" ); watchdog_init(0); hotplug("/etc/hotplug.json" ); procd_coldplug(); break ; case STATE_INIT: watchdog_init(0); LOG("- ubus -\n" ); procd_connect_ubus(); LOG("- init -\n" ); service_init(); service_start_early("ubus" , ubus_cmd); procd_inittab(); procd_inittab_run("respawn" ); procd_inittab_run("askconsole" ); procd_inittab_run("askfirst" ); procd_inittab_run("sysinit" ); break ; case STATE_RUNNING: LOG("- init complete -\n" ); break ; case STATE_SHUTDOWN: LOG("- shutdown -\n" ); procd_inittab_run("shutdown" ); sync(); break ; case STATE_HALT: LOG("- reboot -\n" ); reboot(reboot_event); break ; default : ERROR("Unhandled state %d\n" , state); return ; }; }
二 shell脚本启动:
openwrt是通过一系列shell脚本进行启动流程的组织,下面是启动流程的提纲。如
果想详细了解启动的过程,则需要仔细走读脚本文件。 1. 在make menuconfig 选择target平台 Broadcom BCM947xx/953xx [2.4] 2. linux内核的配置文件由下面两个文件组成 target/linux/generic-2.4/config-default target/linux/brcm-2.4/config-default 3. 在配置文件中可以看到 CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=squashfs,jffs2 init=/etc/preinit noinitrd console=ttyS0,115200" 因此,linux内核启动后,首先运行 /etc/preinit 脚本 4. preinit脚本位置在
package/base-files/files/etc/preinit
#!/bin/sh # Copyright (C) 2006 OpenWrt.org # Copyright (C) 2010 Vertical Communications [ -z "$PREINIT" ] && exec /sbin/init export PATH=/bin:/sbin:/usr/bin:/usr/sbin pi_ifname= pi_ip=192.168.1.1 pi_broadcast=192.168.1.255 pi_netmask=255.255.255.0 fs_failsafe_ifname= fs_failsafe_ip=192.168.1.1 fs_failsafe_broadcast=192.168.1.255 fs_failsafe_netmask=255.255.255.0 fs_failsafe_wait_timeout=2 pi_suppress_stderr="y" pi_init_suppress_stderr="y" pi_init_path="/bin:/sbin:/usr/bin:/usr/sbin" pi_init_cmd="/sbin/init" . /lib/functions.sh boot_hook_init preinit_essential boot_hook_init preinit_main boot_hook_init failsafe boot_hook_init initramfs boot_hook_init preinit_mount_root for pi_source_file in /lib/preinit/*; do . $pi_source_file done boot_run_hook preinit_essential pi_mount_skip_next=false pi_jffs2_mount_success=false pi_failsafe_net_message=false boot_run_hook preinit_main
5. preinit脚本是一系列脚本的入口,这一系列脚本放在下面的目录: package/base-files/files/lib/preinit target/linux/brcm-2.4/base-files/lib/preinit 编译完成后,会统一放在rootfs的/lib/preinit目录下, 03_init_hotplug_failsafe_brcm 40_init_shm 05_failsafe_config_switch_brcm 40_mount_devpts 05_init_interfaces_brcm 40_mount_jffs2 05_mount_skip 40_run_failsafe_hook 05_set_failsafe_switch_brcm 41_merge_overlay_hooks 10_check_for_mtd 50_choose_console 10_essential_fs 50_indicate_regular_preinit 10_indicate_failsafe 60_init_hotplug 10_indicate_preinit 70_initramfs_test 15_mount_proc_brcm 70_pivot_jffs2_root 15_set_preinit_interface_brcm 80_mount_root 20_check_jffs2_ready 90_init_console 20_device_fs_mount 90_mount_no_jffs2 20_failsafe_net_echo 90_restore_config 20_failsafe_set_boot_wait_brcm 99_10_failsafe_login 30_device_fs_daemons 99_10_mount_no_mtd 30_failsafe_wait 99_10_run_init 由于脚本众多,因此openwrt的设计者将这些脚本分成下面几类: preinit_essential preinit_main failsafe initramfs preinit_mount_root 每一类函数按照脚本的开头数字的顺序运行。 6. preinit则执行下面的两类脚本 boot_run_hook preinit_essential boot_run_hook preinit_main 7. preinit执行的最后一个脚本为99_10_run_init,运行 exec env - PATH=$pi_init_path $pi_init_env $pi_init_cmd pi_init_cmd为 pi_init_cmd="/sbin/init" 因此开始运行busybox的init命令 8. busybox的init命令执行inittab的脚本,该脚本来自 package/base-files/files/etc/inittab ::sysinit:/etc/init.d/rcS S boot ::shutdown:/etc/init.d/rcS K stop tts/0::askfirst:/bin/ash --login ttyS0::askfirst:/bin/ash --login tty1::askfirst:/bin/ash --login sysinit为系统初始化运行的 /etc/init.d/rcS S boot脚本 shutdown为系统重启或关机运行的脚本 tty开头的是,如果用户通过串口或者telnet登录,则运行/bin/ash --login askfirst和respawn相同,只是在运行前提示" Please press Enter to activate this console ." 9. 当前启动转到运行 /etc/init.d/rcS S boot,该脚本来自 package/base-files/files/etc/init.d/rcS 和preinit类似,rcS也是一系列脚本的入口,其运行/etc/rc.d目录下S开头的的所 有脚本(如果运行rcS K stop,则运行K开头的所有脚本) K50dropbear S02nvram S40network S50dropbear S96led K90network S05netconfig S41wmacfixup S50telnet S97watchdog K98boot S10boot S45firewall S60dnsmasq S98sysntpd K99umount S39usb S50cron S95done S99sysctl 上面的脚本文件来自: package/base-files/files/etc/init.d target/linux/brcm-2.4/base-files/etc/init.d 还有一些脚本来自各个模块,在install时拷贝到rootfs,比如dropbear模块 package/dropbear/files/dropbear.init 这些脚本先拷贝到/etc/init.d下,然后通过 /etc/rc.common 脚本,将init.d的脚 本链接到/etc/rc.d目录下,并且根据 这些脚本中的START和STOP的关键字,添加 K${STOP}和S${START}的前缀,这样就决定了脚本的先后的运行次序。 10.可以看出, openwrt的启动主要是两个阶段,preinit主要是完成系统的初始化 (如文件系统的准备、模块的加载),rcS主要依次 启动各个模块。
附:脚本走读的一些技巧
a. rootfs目录在build_dir/target-mipsel_uClibc-0.9.30.1/root-brcm-2.4,可以直接在该目录下走读shell脚本。
b. openwrt的shell脚本比较复杂,因此看脚本时可以通过添加 set -x和echo等命令,直接看shell脚本的结果,而不要花太多的时间硬看脚本,主要是理解其主要的意思和设计思路。
转自:http://blog.csdn.net/hui523hui523hui523/article/details/38372119