1. 程式人生 > >openwrt啟動指令碼分析

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一致時表示重啟現有內核