openwrt启动脚本分析

一 内核启动

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)

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <pre name="code" class="cpp">asmlinkage void __init start_kernel(void)  
  2. {  
  3.     char * command_line;  
  4.     extern const struct kernel_param __start___param[], __stop___param[];  
  5.   
  6.     /* 
  7.      * Need to run as early as possible, to initialize the 
  8.      * lockdep hash: 
  9.      */  
  10.     lockdep_init();  
  11.     smp_setup_processor_id();  
  12.     debug_objects_early_init();  
  13.   
  14.     /* 
  15.      * Set up the the initial canary ASAP: 
  16.      */  
  17.     boot_init_stack_canary();  
  18.   
  19.     cgroup_init_early();  
  20.   
  21.     local_irq_disable();  
  22.     early_boot_irqs_disabled = true;  
  23.   
  24. /* 
  25.  * Interrupts are still disabled. Do necessary setups, then 
  26.  * enable them 
  27.  */  
  28.     boot_cpu_init();  
  29.     page_address_init();  
  30.     pr_notice("%s", linux_banner);//-->[    0.000000] Linux version 3.10.36 (hui@ubuntu) (gcc version 4.8.3 (OpenWrt/Linaro GCC 4.8-2014.04 r40773) ) #51 Wed Aug 20 07:55:04 PDT 2014  
  31.     setup_arch(&command_line);  
  32.     mm_init_owner(&init_mm, &init_task);  
  33.     mm_init_cpumask(&init_mm);  
  34.     setup_command_line(command_line);  
  35.     setup_nr_cpu_ids();  
  36.     setup_per_cpu_areas();  
  37.     smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */  
  38.   
  39.     build_all_zonelists(NULL, NULL);  
  40.     page_alloc_init();  
  41.   
  42.     pr_notice("Kernel command line: %s\n", boot_command_line);//-->[    0.000000] Kernel command line: console=ttyS0,57600 rootfstype=squashfs,jffs2  
  43.     parse_early_param();  
  44.     parse_args("Booting kernel", static_command_line, __start___param,  
  45.            __stop___param - __start___param,  
  46.            -1, -1, &unknown_bootoption);  
  47.   
  48.     jump_label_init();  
  49.   
  50.     /* 
  51.      * These use large bootmem allocations and must precede 
  52.      * kmem_cache_init() 
  53.      */  
  54.     setup_log_buf(0);  
  55.     pidhash_init();  
  56.     vfs_caches_init_early();  
  57.     sort_main_extable();  
  58.     trap_init();  
  59.     mm_init();  
  60.   
  61.     /* 
  62.      * Set up the scheduler prior starting any interrupts (such as the 
  63.      * timer interrupt). Full topology setup happens at smp_init() 
  64.      * time - but meanwhile we still have a functioning scheduler. 
  65.      */  
  66.     sched_init();  
  67.     /* 
  68.      * Disable preemption - early bootup scheduling is extremely 
  69.      * fragile until we cpu_idle() for the first time. 
  70.      */  
  71.     preempt_disable();  
  72.     if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n"))  
  73.         local_irq_disable();  
  74.     idr_init_cache();  
  75.     perf_event_init();  
  76.     rcu_init();  
  77.     tick_nohz_init();  
  78.     radix_tree_init();  
  79.     /* init some links before init_ISA_irqs() */  
  80.     early_irq_init();  
  81.     init_IRQ();  
  82.     tick_init();  
  83.     init_timers();  
  84.     hrtimers_init();  
  85.     softirq_init();  
  86.     timekeeping_init();  
  87.     time_init();  
  88.     profile_init();  
  89.     call_function_init();  
  90.     WARN(!irqs_disabled(), "Interrupts were enabled early\n");  
  91.     early_boot_irqs_disabled = false;  
  92.     local_irq_enable();  
  93.   
  94.     kmem_cache_init_late();  
  95.   
  96.     /* 
  97.      * HACK ALERT! This is early. We're enabling the console before 
  98.      * we've done PCI setups etc, and console_init() must be aware of 
  99.      * this. But we do want output early, in case something goes wrong. 
  100.      */  
  101.     console_init();  
  102.     if (panic_later)  
  103.         panic(panic_later, panic_param);  
  104.   
  105.     lockdep_info();  
  106.   
  107.     /* 
  108.      * Need to run this when irqs are enabled, because it wants 
  109.      * to self-test [hard/soft]-irqs on/off lock inversion bugs 
  110.      * too: 
  111.      */  
  112.     locking_selftest();  
  113.   
  114. #ifdef CONFIG_BLK_DEV_INITRD  
  115.     if (initrd_start && !initrd_below_start_ok &&  
  116.         page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {  
  117.         pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",  
  118.             page_to_pfn(virt_to_page((void *)initrd_start)),  
  119.             min_low_pfn);  
  120.         initrd_start = 0;  
  121.     }  
  122. #endif  
  123.     page_cgroup_init();  
  124.     debug_objects_mem_init();  
  125.     kmemleak_init();  
  126.     setup_per_cpu_pageset();  
  127.     numa_policy_init();  
  128.     if (late_time_init)  
  129.         late_time_init();  
  130.     sched_clock_init();  
  131.     calibrate_delay();  
  132.     pidmap_init();  
  133.     anon_vma_init();  
  134. #ifdef CONFIG_X86  
  135.     if (efi_enabled(EFI_RUNTIME_SERVICES))  
  136.         efi_enter_virtual_mode();  
  137. #endif  
  138.     thread_info_cache_init();  
  139.     cred_init();  
  140.     fork_init(totalram_pages);  
  141.     proc_caches_init();  
  142.     buffer_init();  
  143.     key_init();  
  144.     security_init();  
  145.     dbg_late_init();  
  146.     vfs_caches_init(totalram_pages);  
  147.     signals_init();  
  148.     /* rootfs populating might need page-writeback */  
  149.     page_writeback_init();  
  150. #ifdef CONFIG_PROC_FS  
  151.     proc_root_init();  
  152. #endif  
  153.     cgroup_init();  
  154.     cpuset_init();  
  155.     taskstats_init_early();  
  156.     delayacct_init();  
  157.   
  158.     check_bugs();  
  159.   
  160.     acpi_early_init(); /* before LAPIC and SMP init */  
  161.     sfi_init_late();  
  162.   
  163.     if (efi_enabled(EFI_RUNTIME_SERVICES)) {  
  164.         efi_late_init();  
  165.         efi_free_boot_services();  
  166.     }  
  167.   
  168.     ftrace_init();  
  169.   
  170.     /* Do the rest non-__init'ed, we're now alive */  
  171.     rest_init(); //------------------------------------------------->  
  172. }  


在这里,启动应用程序

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static int __ref kernel_init(void *unused)  
  2. {  
  3.     kernel_init_freeable();  
  4.     /* need to finish all async __init code before freeing the memory */  
  5.     async_synchronize_full();  
  6.     free_initmem();  
  7.     mark_rodata_ro();  
  8.     system_state = SYSTEM_RUNNING;  
  9.     numa_default_policy();  
  10.   
  11.     flush_delayed_fput();  
  12.   
  13.     if (ramdisk_execute_command) {  
  14.         if (!run_init_process(ramdisk_execute_command))  
  15.             return 0;  
  16.         pr_err("Failed to execute %s\n", ramdisk_execute_command);  
  17.     }  
  18.   
  19.     /* 
  20.      * We try each of these until one succeeds. 
  21.      * 
  22.      * The Bourne shell can be used instead of init if we are 
  23.      * trying to recover a really broken machine. 
  24.      */  
  25.     if (execute_command) {  
  26.         if (!run_init_process(execute_command))  
  27.             return 0;  
  28.         pr_err("Failed to execute %s.  Attempting defaults...\n",  
  29.             execute_command);  
  30.     }  
  31.     if (!run_init_process("/etc/preinit") ||  
  32.         !run_init_process("/sbin/init") ||  
  33.         !run_init_process("/etc/init") ||  
  34.         !run_init_process("/bin/init") ||  
  35.         !run_init_process("/bin/sh"))  
  36.         return 0;  
  37.   
  38.     panic("No init found.  Try passing init= option to kernel. "  
  39.           "See Linux Documentation/init.txt for guidance.");  
  40. }  


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static noinline void __init kernel_init_freeable(void)  
  2. {  
  3.     /* 
  4.      * Wait until kthreadd is all set-up. 
  5.      */  
  6.     wait_for_completion(&kthreadd_done);  
  7.   
  8.     /* Now the scheduler is fully set up and can do blocking allocations */  
  9.     gfp_allowed_mask = __GFP_BITS_MASK;  
  10.   
  11.     /* 
  12.      * init can allocate pages on any node 
  13.      */  
  14.     set_mems_allowed(node_states[N_MEMORY]);  
  15.     /* 
  16.      * init can run on any cpu. 
  17.      */  
  18.     set_cpus_allowed_ptr(current, cpu_all_mask);  
  19.   
  20.     cad_pid = task_pid(current);  
  21.   
  22.     smp_prepare_cpus(setup_max_cpus);  
  23.   
  24.     do_pre_smp_initcalls();  
  25.     lockup_detector_init();  
  26.   
  27.     smp_init();  
  28.     sched_init_smp();  
  29.   
  30.     do_basic_setup();  
  31.   
  32.     /* Open the /dev/console on the rootfs, this should never fail */  
  33.     if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)  
  34.         pr_err("Warning: unable to open an initial console.\n");  
  35.   
  36.     (void) sys_dup(0);  
  37.     (void) sys_dup(0);  
  38.     /* 
  39.      * check if there is an early userspace init.  If yes, let it do all 
  40.      * the work 
  41.      */  
  42.   
  43.     if (!ramdisk_execute_command)  
  44.         ramdisk_execute_command = "/init";  
  45.   
  46.     if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {  
  47.         ramdisk_execute_command = NULL;  
  48.         prepare_namespace();  
  49.     }  
  50.   
  51.     /* 
  52.      * Ok, we have completed the initial bootup, and 
  53.      * we're essentially up and running. Get rid of the 
  54.      * initmem segments and start the user-mode stuff.. 
  55.      */  
  56.   
  57.     /* rootfs is available now, try loading default modules */  
  58.     load_default_modules();  
  59. }  

启动第一个应用,init

trunk\build_dir\target-mipsel_24kec+dsp_uClibc-0.9.33.2\procd-2014-03-18\initd\init.c

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int  
  2. main(int argc, char **argv)  
  3. {  
  4.     pid_t pid;  
  5.   
  6.     sigaction(SIGTERM, &sa_shutdown, NULL);  
  7.     sigaction(SIGUSR1, &sa_shutdown, NULL);  
  8.     sigaction(SIGUSR2, &sa_shutdown, NULL);  
  9.   
  10.     early();//-------->early.c  
  11.     cmdline();  
  12.     watchdog_init(1); //------->../watchdog.c  
  13.   
  14.     pid = fork();  
  15.     if (!pid) {  
  16.         char *kmod[] = { "/sbin/kmodloader""/etc/modules-boot.d/", NULL };  
  17.   
  18.         if (debug < 3) {  
  19.             int fd = open("/dev/null", O_RDWR);  
  20.   
  21.             if (fd > -1) {  
  22.                 dup2(fd, STDIN_FILENO);  
  23.                 dup2(fd, STDOUT_FILENO);  
  24.                 dup2(fd, STDERR_FILENO);  
  25.                 if (fd > STDERR_FILENO)  
  26.                     close(fd);  
  27.             }  
  28.         }  
  29.         execvp(kmod[0], kmod);  
  30.         ERROR("Failed to start kmodloader\n");  
  31.         exit(-1);  
  32.     }  
  33.     if (pid <= 0)  
  34.         ERROR("Failed to start kmodloader instance\n");  
  35.     uloop_init();  
  36.     preinit();    //-------------->watchdog.c  
  37.     uloop_run();  
  38.   
  39.     return 0;  
  40. }  

trunk\build_dir\target-mipsel_24kec+dsp_uClibc-0.9.33.2\procd-2014-03-18\state.c

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static void state_enter(void)  
  2. {  
  3.     char ubus_cmd[] = "/sbin/ubusd";  
  4.   
  5.     switch (state) {  
  6.     case STATE_EARLY:  
  7.         LOG("- early -\n");  
  8.         watchdog_init(0);  
  9.         hotplug("/etc/hotplug.json");  
  10.         procd_coldplug();  
  11.         break;  
  12.   
  13.     case STATE_INIT:  
  14.         // try to reopen incase the wdt was not available before coldplug  
  15.         watchdog_init(0);  
  16.         LOG("- ubus -\n");  
  17.         procd_connect_ubus();  
  18.   
  19.         LOG("- init -\n");  
  20.         service_init();  
  21.         service_start_early("ubus", ubus_cmd);  
  22.   
  23.         procd_inittab();  
  24.         procd_inittab_run("respawn");  
  25.         procd_inittab_run("askconsole");  
  26.         procd_inittab_run("askfirst");  
  27.         procd_inittab_run("sysinit");  
  28.         break;  
  29.   
  30.     case STATE_RUNNING:  
  31.         LOG("- init complete -\n");  
  32.         break;  
  33.   
  34.     case STATE_SHUTDOWN:  
  35.         LOG("- shutdown -\n");  
  36.         procd_inittab_run("shutdown");  
  37.         sync();  
  38.         break;  
  39.   
  40.     case STATE_HALT:  
  41.         LOG("- reboot -\n");  
  42.         reboot(reboot_event);  
  43.         break;  
  44.   
  45.     default:  
  46.         ERROR("Unhandled state %d\n", state);  
  47.         return;  
  48.     };  
  49. }  


 

二 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

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. # Copyright (C) 2006 OpenWrt.org  
  3. # Copyright (C) 2010 Vertical Communications  
  4.   
  5. [ -z "$PREINIT" ] && exec /sbin/init  
  6.   
  7. export PATH=/bin:/sbin:/usr/bin:/usr/sbin  
  8.   
  9. pi_ifname=  
  10. pi_ip=192.168.1.1  
  11. pi_broadcast=192.168.1.255  
  12. pi_netmask=255.255.255.0  
  13.   
  14. fs_failsafe_ifname=  
  15. fs_failsafe_ip=192.168.1.1  
  16. fs_failsafe_broadcast=192.168.1.255  
  17. fs_failsafe_netmask=255.255.255.0  
  18.   
  19. fs_failsafe_wait_timeout=2  
  20.   
  21. pi_suppress_stderr="y"  
  22. pi_init_suppress_stderr="y"  
  23. pi_init_path="/bin:/sbin:/usr/bin:/usr/sbin"  
  24. pi_init_cmd="/sbin/init"  
  25.   
  26. . /lib/functions.sh  
  27.   
  28. boot_hook_init preinit_essential  
  29. boot_hook_init preinit_main  
  30. boot_hook_init failsafe  
  31. boot_hook_init initramfs  
  32. boot_hook_init preinit_mount_root  
  33.   
  34. for pi_source_file in /lib/preinit/*; do  
  35.     . $pi_source_file  
  36. done  
  37.   
  38. boot_run_hook preinit_essential  
  39.   
  40. pi_mount_skip_next=false  
  41. pi_jffs2_mount_success=false  
  42. pi_failsafe_net_message=false  
  43.   
  44. 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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值