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)
<pre name="code" class="cpp">asmlinkage void __init start_kernel(void) { char * command_line; extern const struct kernel_param __start___param[], __stop___param[]; /* * Need to run as early as possible, to initialize the * lockdep hash: */ lockdep_init(); 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);//-->[ 0.000000] Linux version 3.10.36 (
[email protected]) (gcc version 4.8.3 (OpenWrt/Linaro GCC 4.8-2014.04 r40773) ) #51 Wed Aug 20 07:55:04 PDT 2014 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(); /* arch-specific boot-cpu hooks */ build_all_zonelists(NULL, NULL); page_alloc_init(); pr_notice("Kernel command line: %s\n", boot_command_line);//-->[ 0.000000] Kernel command line: console=ttyS0,57600 rootfstype=squashfs,jffs2 parse_early_param(); parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, -1, -1, &unknown_bootoption); 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(); perf_event_init(); rcu_init(); tick_nohz_init(); radix_tree_init(); /* init some links before init_ISA_irqs() */ 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(); /* * 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(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_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(); /* rootfs populating might need page-writeback */ 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(); /* before LAPIC and SMP 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(); //-------------------------------------------------> }
在這裡,啟動應用程式
static int __ref kernel_init(void *unused) { kernel_init_freeable(); /* need to finish all async __init code before freeing the memory */ 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); } /* * We try each of these until one succeeds. * * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. */ 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 until kthreadd is all set-up.
*/
wait_for_completion(&kthreadd_done);
/* Now the scheduler is fully set up and can do blocking allocations */
gfp_allowed_mask = __GFP_BITS_MASK;
/*
* init can allocate pages on any node
*/
set_mems_allowed(node_states[N_MEMORY]);
/*
* init can run on any cpu.
*/
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();
/* Open the /dev/console on the rootfs, this should never fail */
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);
/*
* 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..
*/
/* rootfs is available now, try loading default modules */
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();//-------->early.c
cmdline();
watchdog_init(1); //------->../watchdog.c
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(); //-------------->watchdog.c
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:
// try to reopen incase the wdt was not available before coldplug
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指令碼的結果,而不要花太多的時間硬看指令碼,主要是理解其主要的意思和設計思路。
相關推薦
openwrt啟動指令碼分析
一 核心啟動 uboot -> start_kernel -> rest_init() -> kernel_thread(kernel_init)-->kernel_init_freeable()-->run_init_process
OpenWrt啟動過程分析+新增自啟動指令碼[轉載]
一、OpenWrt啟動過程分析 總結一下OpenWrt的啟動流程:1.CFE->2.linux->3./etc/preinit->4./sbin/init ->5./etc/inittab ->6./etc/init.d/rcS->7./etc/rc.d/S*
Openwrt啟動流程及啟動指令碼分析
1 概述 在linux的發展過程中,linux的啟動程式也在發展,從sysv init到現在的upstart、systemd, 通常該程式是程序號為1的程序,該程式在linux系統有著舉足輕重的地方。在openwrt中,使 用了另外一種啟動程式叫做pr
OpenWrt啟動過程分析+新增自啟動指令碼
http://blog.csdn.net/jk110333/article/details/39529459 總結一下OpenWrt的啟動流程:1.CFE->2.linux->3./etc/preinit->4./sbin/init ->5./et
OpenWrt啟動過程分析+新增自啟動指令碼########33 openwrt blog
####################### http://blog.chinaunix.net/uid/26675482/cid-200203-list-1.html ------###33 openwrt blog ##############
linux下的pycharm啟動指令碼分析---即pycharm.sh
pycharm的啟動指令碼分析------即pycharm.sh 1.分析一下pycharm的啟動指令碼,即pycharm.sh 看看他們是怎麼寫的,廢話不說上圖 message函式的功能是,當啟動pycharm出錯的時候,對使用者進行提示 確保指令碼中用
OPenwrt 啟動程序分析
PID USER VSZ STAT COMMAND 1 root 1416 S /sbin/procd
OpenWrt啟動過程分析
總結一下OpenWrt的啟動流程:1.CFE->2.linux->3./etc/preinit->4./sbin/init ->5./etc/inittab ->6./etc/init.d/rcS->7./etc/rc.d/S* -&g
tomcat啟動指令碼startup.sh分析
一、分析說明 為了寫出更加完善的tomcat啟動方面的自動化指令碼,健壯自己用於程式碼上線自動化部署的指令碼,特分析下tomcat的bin目錄下的starup.sh指令碼,學習標準的sh指令碼的編寫方法,從中吸取經驗 二、指令碼分析 #!/bin/sh #
分析啟動指令碼zkServer.sh
# use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" ZOOBIN="$(dirname "${ZOOBIN}")" ZOOBINDIR="$(cd "${ZO
Openwrt的wifi指令碼分析
首先分享一下/sbin/wifi指令碼 #!/bin/sh # Copyright (C) 2006 OpenWrt.org
Openwrt啟動後的指令碼執行(二)
Openwrt啟動指令碼的執行分為兩個階段, preinit完成系統的初始化, rcS依次啟動了各個應用程式模組。 執行/etc/preinit指令碼之後 boot_hook_init preinit_essential boot_hook_init
【轉】Android 4.0 Launcher2源碼分析——啟動過程分析
handler flag 這一 第一次啟動 asynctask pla size ontouch wait Android的應用程序的入口定義在AndroidManifest.xml文件中可以找出:[html] <manifest xmlns:android="htt
X86架構下Linux啟動過程分析
重要 ack csdn 檢查 point article span 註意 eap 1、X86架構下的從開機到Start_kernel啟動的整體過程 這個過程簡要概述為: 開機——>BIOS——>GRUB/LILO——>Linux Kernel
Linux開機啟動過程分析
物理內存 登錄 page thread 陷阱門 execute 啟動過程 font 定義 Linux開機啟動過程分析 開機過程指的是從打開計算機電源直到LINUX顯示用戶登錄畫面的全過程。分析LINUX開機過程也是深入了解LINUX核心工作原理的一個很好的途徑。 啟動第一
u-boot-201611 啟動過程分析——基於smdk2410
u-bootu-boot-201611 啟動過程分析——基於smdk2410
Android5 Zygote 與 SystemServer 啟動流程分析
進一步 null 正常的 rtb 這樣的 ket constant vml resp Android5 Zygote 與 SystemServer 啟動流程分析 Android5 Zygote 與 SystemServer 啟動流程分析 前言 zy
linux 系統啟動過程分析
系統root 密碼丟失故障 linux啟動順序主板BIOS加電自檢 檢查硬件--> 讀取硬盤引導扇區(MBR)--> 啟動引導程序(grub)--> 選擇系統--> 加載系統內核(kernel shell)--> 啟動系統讀取相應的默認設置(環境變量,運行級別)--
開機啟動流程分析
boot 啟動流程 本節索引 在對系統啟動流程進行分析的時候,我想你一定是對系統有了一定的了解。系統的啟動目前來講大都為串行接力的方式來啟動。而所謂的並行方式的啟動方式也是某一個階段的並行。所以我按照系統啟動的順序來把文章連綴起來。 * BIOS階段 * BootLoader階段
kexec 內核快速啟動流程分析
-- 令行 並且 內存 tab 執行過程 family use -a 一、命令行 1. kexec -l $kpwd --append="$arg" 其中$kpwd =目標內核的路徑 $arg =傳給內核的參數,與/proc/cmdline一致時表示重啟現有內核