linux Init分析(原创)
2013-10-30 20:42
281 查看
1.uboot的目标就是启动内核kernel;
2.kernel的目的就是启动应用程序,而第一个应用程序即是Init,构建根文件系统。
从uboot初始化配置后,引导内核的启动,启动函数为:start_kernel(void)
其他可以先不管,我们需要的是看看rest_init()函数;
代码如下:
rest_init()函数,如下:
我们可以看到内核有一个内核的初始化线程:
进入看看到底是实现什么东西:
上面代码实现的是控制台的输入,输出,还有error,好,现在,目前为止还没有看到真正的好东西,接着往下看
好东西终于出现了:
static int __init init_setup(char *str)
{
unsigned int i;
execute_command = str;
for (i = 1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return 1;
}
__setup("init=", init_setup);//设置命令行参数时候如:init=/linuxrc
查看一下:
# ls /linuxrc -l
lrwxrwxrwx 1 root root 11 Oct 25 2013 /linuxrc -> bin/busybox
那么linux就先启动[b]/linuxrc[/b]
# ls /sbin/init -l
lrwxrwxrwx 1 root root 14 Oct 25 2013 /sbin/init -> ../bin/busybox
/sbin/init 指向/bin/busybox ;
至此我们发现所启动的程序都是指向busybox本身。因此我们需要分析busybox本身才能知道到底发生了什么.
解压busybox-1.16.1 建立工程。我们找到init.c的文件。当然里面也有cp.c ls.c等文件,linux是借助busybox来实现这些命令的,如果需要在应用程序里面实现某些类似功能可以参考busybox的源码。
我们先看init.c里面的main函数:
看看都做了什么东西:
首先看一下
前面两个冒号空的不用管,ctrlaltdel shutdown 是一下组合键或者操作的动作的信号发生后才运行的。
我们找到这句:new_init_action(1 << action, token[3], tty);
我们发现一个很重要的一项:
这里涉及一个udev机制,详细请查看文章mdev解析,这里意思是将linux虚拟的文件系统proc sysfs tmpfs 挂载到对应的目录这样,我们就不必要手动去创建了。
上面的for循环是干嘛用的呢,我们可以大胆猜测下,应该是if(满足某种条件如具备可执行条件)就循环执行一个目录下的脚本。
我们去看看/etc/init.d/目录下都有哪些脚本:
我们挑一些来看看,如S90modules,发现它的作用就是启动/kmod/load脚本,我们再看看具体都做了哪些东西:
我们不难知道,load脚本主要是执行一些驱动模块的卸载,加载,还有将/dev下的一些分区用正确的格式挂载到对应的目录下。
至此,我们的分析基本完毕,另外还有app的启动也是用脚本,在进入linux时候启动起来的。
/etc/profile 文件是登陆linux时候就运行的,app此时启动了。
# cat /etc/fstab proc /proc proc defaults 0 0sysfs /sys sysfs defaults 0 0tmpfs /dev tmpfs defaults 0 0
2.kernel的目的就是启动应用程序,而第一个应用程序即是Init,构建根文件系统。
从uboot初始化配置后,引导内核的启动,启动函数为:start_kernel(void)
其他可以先不管,我们需要的是看看rest_init()函数;
代码如下:
asmlinkage void __init start_kernel(void) { char * command_line; extern struct kernel_param __start___param[], __stop___param[]; smp_setup_processor_id(); /* * Need to run as early as possible, to initialize the * lockdep hash: */ lockdep_init(); debug_objects_early_init(); /* * Set up the the initial canary ASAP: */ boot_init_stack_canary(); cgroup_init_early(); local_irq_disable(); early_boot_irqs_off(); early_init_irq_lock_class(); /* * Interrupts are still disabled. Do necessary setups, then * enable them */ lock_kernel(); tick_init(); boot_cpu_init(); page_address_init(); printk(KERN_NOTICE "%s", linux_banner); setup_arch(&command_line); mm_init_owner(&init_mm, &init_task); 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); page_alloc_init(); printk(KERN_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, &unknown_bootoption); /* * These use large bootmem allocations and must precede * kmem_cache_init() */ 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 (!irqs_disabled()) { printk(KERN_WARNING "start_kernel(): bug: interrupts were " "enabled *very* early, fixing it\n"); local_irq_disable(); } rcu_init(); radix_tree_init(); /* init some links before init_ISA_irqs() */ early_irq_init(); init_IRQ(); prio_tree_init(); init_timers(); hrtimers_init(); softirq_init(); timekeeping_init(); time_init(); profile_init(); if (!irqs_disabled()) printk(KERN_CRIT "start_kernel(): bug: interrupts were " "enabled early\n"); early_boot_irqs_on(); local_irq_enable(); /* Interrupts are enabled now so all GFP allocations are safe. */ gfp_allowed_mask = __GFP_BITS_MASK; 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) { printk(KERN_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(); enable_debug_pagealloc(); kmemtrace_init(); kmemleak_init(); debug_objects_mem_init(); idr_init_cache(); 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_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(); ftrace_init(); /* Do the rest non-__init'ed, we're now alive */ rest_init(); }
rest_init()函数,如下:
static noinline void __init_refok rest_init(void) __releases(kernel_lock) { int pid; rcu_scheduler_starting(); /* * 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 | CLONE_SIGHAND); 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); unlock_kernel(); /* * The boot idle thread must execute schedule() * at least once to get things moving: */ init_idle_bootup_task(current); preempt_enable_no_resched(); schedule(); preempt_disable(); /* Call into cpu_idle with preempt disabled */ cpu_idle(); }
我们可以看到内核有一个内核的初始化线程:
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
进入看看到底是实现什么东西:
static int __init kernel_init(void * unused) { /* * Wait until kthreadd is all set-up. */ wait_for_completion(&kthreadd_done); lock_kernel(); /* * init can allocate pages on any node */ set_mems_allowed(node_states[N_HIGH_MEMORY]); /* * init can run on any cpu. */ set_cpus_allowed_ptr(current, cpu_all_mask); /* * Tell the world that we're going to be the grim * reaper of innocent orphaned children. * * We don't want people to have to make incorrect * assumptions about where in the task array this * can be found. */ init_pid_ns.child_reaper = current; cad_pid = task_pid(current); smp_prepare_cpus(setup_max_cpus); do_pre_smp_initcalls(); start_boot_trace(); 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) printk(KERN_WARNING "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.. */ init_post(); return 0; }
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) printk(KERN_WARNING "Warning: unable to open an initial console.\n"); (void) sys_dup(0); (void) sys_dup(0);
上面代码实现的是控制台的输入,输出,还有error,好,现在,目前为止还没有看到真正的好东西,接着往下看
init_post();
static noinline int init_post(void) __releases(kernel_lock) { /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); free_initmem(); unlock_kernel(); mark_rodata_ro(); system_state = SYSTEM_RUNNING; numa_default_policy(); current->signal->flags |= SIGNAL_UNKILLABLE; if (ramdisk_execute_command) { run_init_process(ramdisk_execute_command); printk(KERN_WARNING "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) { run_init_process(execute_command); printk(KERN_WARNING "Failed to execute %s. Attempting " "defaults...\n", execute_command); } run_init_process("/sbin/init"); run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh"); panic("No init found. Try passing init= option to kernel. " "See Linux Documentation/init.txt for guidance."); }
好东西终于出现了:
if (ramdisk_execute_command) { run_init_process(ramdisk_execute_command); printk(KERN_WARNING "Failed to execute %s\n", ramdisk_execute_command); } 这个是干啥用的呢,搜索一下发现、
static int __init init_setup(char *str)
{
unsigned int i;
execute_command = str;
for (i = 1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return 1;
}
__setup("init=", init_setup);//设置命令行参数时候如:init=/linuxrc
查看一下:
# ls /linuxrc -l
lrwxrwxrwx 1 root root 11 Oct 25 2013 /linuxrc -> bin/busybox
那么linux就先启动[b]/linuxrc[/b]
run_init_process("/sbin/init"); run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh"); 从函数名字来看run_init_process(“”)函数是用来初始化init进程的。在众多的书籍中,都介绍着,我们的linux系统最先启动的是init进程,我们可以直观的ps命令知道,init的pid = 1 因此我们有理由相信至此,我们找到好东西了。 首先 我们在linux的shell下
# ls /sbin/init -l
lrwxrwxrwx 1 root root 14 Oct 25 2013 /sbin/init -> ../bin/busybox
/sbin/init 指向/bin/busybox ;
至此我们发现所启动的程序都是指向busybox本身。因此我们需要分析busybox本身才能知道到底发生了什么.
解压busybox-1.16.1 建立工程。我们找到init.c的文件。当然里面也有cp.c ls.c等文件,linux是借助busybox来实现这些命令的,如果需要在应用程序里面实现某些类似功能可以参考busybox的源码。
我们先看init.c里面的main函数:
看看都做了什么东西:
int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int init_main(int argc UNUSED_PARAM, char **argv) { die_sleep = 30 * 24*60*60; /* if xmalloc would ever die... */ if (argv[1] && strcmp(argv[1], "-q") == 0) { return kill(1, SIGHUP); } if (!DEBUG_INIT) { /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ if (getpid() != 1 && (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc")) ) { bb_show_usage(); } /* Turn off rebooting via CTL-ALT-DEL - we get a * SIGINT on CAD so we can shut things down gracefully... */ reboot(RB_DISABLE_CAD); /* misnomer */ } /* Figure out where the default console should be */ console_init(); set_sane_term(); xchdir("/"); setsid(); /* Make sure environs is set to something sane */ putenv((char *) "HOME=/"); putenv((char *) bb_PATH_root_path); putenv((char *) "SHELL=/bin/sh"); putenv((char *) "USER=root"); /* needed? why? */ if (argv[1]) xsetenv("RUNLEVEL", argv[1]); #if !ENABLE_FEATURE_EXTRA_QUIET /* Hello world */ message(L_CONSOLE | L_LOG, "init started: %s", bb_banner); #endif /* Make sure there is enough memory to do something useful. */ if (ENABLE_SWAPONOFF) { struct sysinfo info; if (sysinfo(&info) == 0 && (info.mem_unit ? info.mem_unit : 1) * (long long)info.totalram < 1024*1024 ) { message(L_CONSOLE, "Low memory, forcing swapon"); /* swapon -a requires /proc typically */ new_init_action(SYSINIT, "mount -t proc proc /proc", ""); /* Try to turn on swap */ new_init_action(SYSINIT, "swapon -a", ""); run_actions(SYSINIT); /* wait and removing */ } } /* Check if we are supposed to be in single user mode */ if (argv[1] && (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1')) ) { /* ??? shouldn't we set RUNLEVEL="b" here? */ /* Start a shell on console */ new_init_action(RESPAWN, bb_default_login_shell, ""); } else { /* Not in single user mode - see what inittab says */ /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, * then parse_inittab() simply adds in some default * actions(i.e., INIT_SCRIPT and a pair * of "askfirst" shells */ parse_inittab(); } #if ENABLE_SELINUX if (getenv("SELINUX_INIT") == NULL) { int enforce = 0; putenv((char*)"SELINUX_INIT=YES"); if (selinux_init_load_policy(&enforce) == 0) { BB_EXECVP(argv[0], argv); } else if (enforce > 0) { /* SELinux in enforcing mode but load_policy failed */ message(L_CONSOLE, "can't load SELinux Policy. " "Machine is in enforcing mode. Halting now."); exit(EXIT_FAILURE); } } #endif /* Make the command line just say "init" - thats all, nothing else */ strncpy(argv[0], "init", strlen(argv[0])); /* Wipe argv[1]-argv so they don't clutter the ps listing */ while (*++argv) memset(*argv, 0, strlen(*argv)); /* Set up signal handlers */ if (!DEBUG_INIT) { struct sigaction sa; bb_signals(0 + (1 << SIGUSR1) /* halt */ + (1 << SIGTERM) /* reboot */ + (1 << SIGUSR2) /* poweroff */ , halt_reboot_pwoff); signal(SIGQUIT, restart_handler); /* re-exec another init */ /* Stop handler must allow only SIGCONT inside itself */ memset(&sa, 0, sizeof(sa)); sigfillset(&sa.sa_mask); sigdelset(&sa.sa_mask, SIGCONT); sa.sa_handler = stop_handler; /* NB: sa_flags doesn't have SA_RESTART. * It must be able to interrupt wait(). */ sigaction_set(SIGTSTP, &sa); /* pause */ /* Does not work as intended, at least in 2.6.20. * SIGSTOP is simply ignored by init: */ sigaction_set(SIGSTOP, &sa); /* pause */ /* SIGINT (Ctrl-Alt-Del) must interrupt wait(), * setting handler without SA_RESTART flag. */ bb_signals_recursive_norestart((1 << SIGINT), record_signo); } /* Set up "reread /etc/inittab" handler. * Handler is set up without SA_RESTART, it will interrupt syscalls. */ if (!DEBUG_INIT && ENABLE_FEATURE_USE_INITTAB) bb_signals_recursive_norestart((1 << SIGHUP), record_signo); /* Now run everything that needs to be run */ /* First run the sysinit command */ run_actions(SYSINIT); check_delayed_sigs(); /* Next run anything that wants to block */ run_actions(WAIT); check_delayed_sigs(); /* Next run anything to be run only once */ run_actions(ONCE); /* Now run the looping stuff for the rest of forever. */ while (1) { int maybe_WNOHANG; maybe_WNOHANG = check_delayed_sigs(); /* (Re)run the respawn/askfirst stuff */ run_actions(RESPAWN | ASKFIRST); maybe_WNOHANG |= check_delayed_sigs(); /* Don't consume all CPU time - sleep a bit */ sleep(1); maybe_WNOHANG |= check_delayed_sigs(); /* Wait for any child process(es) to exit. * * If check_delayed_sigs above reported that a signal * was caught, wait will be nonblocking. This ensures * that if SIGHUP has reloaded inittab, respawn and askfirst * actions will not be delayed until next child death. */ if (maybe_WNOHANG) maybe_WNOHANG = WNOHANG; while (1) { pid_t wpid; struct init_action *a; /* If signals happen _in_ the wait, they interrupt it, * bb_signals_recursive_norestart set them up that way */ wpid = waitpid(-1, NULL, maybe_WNOHANG); if (wpid <= 0) break; a = mark_terminated(wpid); if (a) { message(L_LOG, "process '%s' (pid %d) exited. " "Scheduling for restart.", a->command, wpid); } /* See if anyone else is waiting to be reaped */ maybe_WNOHANG = WNOHANG; } } /* while (1) */ }
首先看一下
if (argv[1] 60 && (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1')) 61 ) { 62 /* ??? shouldn't we set RUNLEVEL="b" here? */ 63 /* Start a shell on console */ 64 new_init_action(RESPAWN, bb_default_login_shell, ""); 65 } else { 66 /* Not in single user mode - see what inittab says */ 67 68 /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, 69 * then parse_inittab() simply adds in some default 70 * actions(i.e., INIT_SCRIPT and a pair 71 * of "askfirst" shells */ 72 parse_inittab(); 73 }
argv[1]参数是空的,实际上我们之前启动的内核init进程也是没有参数的,因此linux走的是另外的分支即是:
parse_inittab();
static void parse_inittab(void) { #if ENABLE_FEATURE_USE_INITTAB char *token[4]; parser_t *parser = config_open2("/etc/inittab", fopen_for_read); if (parser == NULL) #endif { /* No inittab file - set up some default behavior */ /* Reboot on Ctrl-Alt-Del */ new_init_action(CTRLALTDEL, "reboot", ""); /* Umount all filesystems on halt/reboot */ new_init_action(SHUTDOWN, "umount -a -r", ""); /* Swapoff on halt/reboot */ if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", ""); /* Prepare to restart init when a QUIT is received */ new_init_action(RESTART, "init", ""); /* Askfirst shell on tty1-4 */ new_init_action(ASKFIRST, bb_default_login_shell, ""); //TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users new_init_action(ASKFIRST, bb_default_login_shell, VC_2); new_init_action(ASKFIRST, bb_default_login_shell, VC_3); new_init_action(ASKFIRST, bb_default_login_shell, VC_4); /* sysinit */ new_init_action(SYSINIT, INIT_SCRIPT, ""); return; } #if ENABLE_FEATURE_USE_INITTAB /* optional_tty:ignored_runlevel:action:command * Delims are not to be collapsed and need exactly 4 tokens */ while (config_read(parser, token, 4, 0, "#:", PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) { /* order must correspond to SYSINIT..RESTART constants */ static const char actions[] ALIGN1 = "sysinit\0""wait\0""once\0""respawn\0""askfirst\0" "ctrlaltdel\0""shutdown\0""restart\0"; int action; char *tty = token[0]; if (!token[3]) /* less than 4 tokens */ goto bad_entry; action = index_in_strings(actions, token[2]); if (action < 0 || !token[3][0]) /* token[3]: command */ goto bad_entry; /* turn .*TTY -> /dev/TTY */ if (tty[0]) { if (strncmp(tty, "/dev/", 5) == 0) tty += 5; tty = concat_path_file("/dev/", tty); } new_init_action(1 << action, token[3], tty); if (tty[0]) free(tty); continue; bad_entry: message(L_LOG | L_CONSOLE, "Bad inittab entry at line %d", parser->lineno); } config_close(parser); #endif }
parser_t *parser = config_open2("/etc/inittab", fopen_for_read);
我们看名字可以得知此语句是去读取配置文件/etc/inittab。如果没有/etc/inittab文件(if (parser == NULL)),我们就给它配置一个默认的inittab文件
海思开发板下看一下:
# cat /etc/inittab # /etc/inittab init(8) configuration for BusyBox # # Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> # # # Note, BusyBox init doesn't support runlevels. The runlevels field is # completely ignored by BusyBox init. If you want runlevels, use sysvinit. # # # Format for each entry: <id>:<runlevels>:<action>:<process> # # <id>: WARNING: This field has a non-traditional meaning for BusyBox init! # # The id field is used by BusyBox init to specify the controlling tty for # the specified process to run on. The contents of this field are # appended to "/dev/" and used as-is. There is no need for this field to # be unique, although if it isn't you may have strange results. If this # field is left blank, it is completely ignored. Also note that if # BusyBox detects that a serial console is in use, then all entries # containing non-empty id fields will _not_ be run. BusyBox init does # nothing with utmp. We don't need no stinkin' utmp. # # <runlevels>: The runlevels field is completely ignored. # # <action>: Valid actions include: sysinit, respawn, askfirst, wait, once, # restart, ctrlaltdel, and shutdown. # # Note: askfirst acts just like respawn, but before running the specified # process it displays the line "Please press Enter to activate this # console." and then waits for the user to press enter before starting # the specified process. # # Note: unrecognised actions (like initdefault) will cause init to emit # an error message, and then go along with its business. # # <process>: Specifies the process to be executed and it's command line. # # Note: BusyBox init works just fine without an inittab. If no inittab is # found, it has the following default behavior: # ::sysinit:/etc/init.d/rcS # ::askfirst:/bin/sh # ::ctrlaltdel:/sbin/reboot # ::shutdown:/sbin/swapoff -a # ::shutdown:/bin/umount -a -r # ::restart:/sbin/init # # if it detects that /dev/console is _not_ a serial console, it will # also run: # tty2::askfirst:/bin/sh # tty3::askfirst:/bin/sh # tty4::askfirst:/bin/sh # # Boot-time system configuration/initialization script. # This is run first except when booting in single-user mode. # ::sysinit:/etc/init.d/rcS # /bin/sh invocations on selected ttys # # Note below that we prefix the shell commands with a "-" to indicate to the # shell that it is supposed to be a login shell. Normally this is handled by # login, but since we are bypassing login in this case, BusyBox lets you do # this yourself... # # Start an "askfirst" shell on the console (whatever that may be) #::askfirst:-/bin/sh # Start an "askfirst" shell on /dev/tty2-4 # tty2::askfirst:-/bin/sh # tty3::askfirst:-/bin/sh # tty4::askfirst:-/bin/sh # /sbin/getty invocations for selected ttys # tty4::respawn:/sbin/getty 38400 tty5 # tty5::respawn:/sbin/getty 38400 tty6 # Example of how to put a getty on a serial line (for a terminal) ::respawn:/sbin/getty -L ttyS000 115200 vt100 -n root -I "Auto login as root ..." #::respawn:/sbin/getty -L ttyS1 9600 vt100 # # Example how to put a getty on a modem line. #::respawn:/sbin/getty 57600 ttyS2 # Stuff to do when restarting the init process ::restart:/sbin/init # Stuff to do before rebooting ::ctrlaltdel:/sbin/reboot ::shutdown:/bin/umount -a -r ::shutdown:/sbin/swapoff -a
前面两个冒号空的不用管,ctrlaltdel shutdown 是一下组合键或者操作的动作的信号发生后才运行的。
我们找到这句:new_init_action(1 << action, token[3], tty);
仔细读源码,我们可以知道它是将/etc/inittab配置文件的每一项执行完后就将其他从链表中删除。 我们在配置文件/etc/inittab发现一个脚本:
::sysinit:/etc/init.d/rcS 看看它想干嘛的:
#! /bin/sh /bin/mount -a echo " _ _ _ _ _ _ _ _ _ _ _ _ \ _ _ _ _ _ ___ / /__/ \ |_/ / __ / - _ ___ / / / / / / _ _ _ _/ / / \_/ \_ ______ ___________\___\__________________ " for initscript in /etc/init.d/S[0-9][0-9]* do if [ -x $initscript ] ; then echo "[RCS]: $initscript" $initscript fi done
我们发现一个很重要的一项:
/bin/mount -a 它的意思是将会执行/etc/fstab脚本。我们在海思平台上看一下:
# cat /etc/fstab proc /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 tmpfs /dev tmpfs defaults 0 0
这里涉及一个udev机制,详细请查看文章mdev解析,这里意思是将linux虚拟的文件系统proc sysfs tmpfs 挂载到对应的目录这样,我们就不必要手动去创建了。
for initscript in /etc/init.d/S[0-9][0-9]* do if [ -x $initscript ] ; then echo "[RCS]: $initscript" $initscript fi done
上面的for循环是干嘛用的呢,我们可以大胆猜测下,应该是if(满足某种条件如具备可执行条件)就循环执行一个目录下的脚本。
我们去看看/etc/init.d/目录下都有哪些脚本:
# cd /etc/init.d/ # ls S00devs S01udev S80network S90modules rcS # cat S90modules #!/bin/sh telnetd& cd /kmod ./load cd -
我们挑一些来看看,如S90modules,发现它的作用就是启动/kmod/load脚本,我们再看看具体都做了哪些东西:
# cat /kmod/load rmmod ufsd rmmod ohci-hcd rmmod ehci-hcd rmmod png rmmod jpge rmmod jpeg rmmod hi_wdg rmmod hi_keyled rmmod hi_e2prom rmmod hi_cipher rmmod hi_ir rmmod hi_sci rmmod hifb rmmod hi_tuner rmmod hi_svdec.ko rmmod hi_mpi rmmod hi_ndpt rmmod hi_otp rmmod tde #rmmod hi_usbprotected rmmod hi_i2c rmmod hi_gpioi2c rmmod hi_gpio rmmod hi_dmac rmmod hi_common rmmod hi_mmz rmmod hi_c51 rmmod hi_media insmod hi_c51.ko insmod hi_mmz.ko insmod hi_common.ko insmod hi_dmac.ko insmod hi_gpio.ko insmod hi_gpioi2c.ko gpioidclock=11 clockbit=3 gpioiddata=12 databit=5 i2cmode=2 insmod hi_i2c.ko #insmod hi_usbprotected.ko Usb0PwrEn=63 Usb0Ovrcur=65 Usb0IntType=1 insmod tde.ko insmod hi_otp.ko insmod hi_ndpt.ko insmod hi_mpi.ko insmod hi_svdec.ko insmod hi_tuner.ko insmod hifb.ko video="hifb:vram0_size:2430, vram2_size:7200" insmod hi_sci.ko insmod hi_ir.ko insmod hi_cipher.ko insmod hi_e2prom.ko insmod hi_keyled.ko insmod hi_wdg.ko insmod jpeg.ko insmod jpge.ko insmod png.ko insmod /kmod/usb/ehci-hcd.ko insmod /kmod/usb/ohci-hcd.ko echo 3 > /proc/sys/vm/dirty_ratio insmod ufsd.ko mount -t squashfs /dev/romblock11 /home mount -t yaffs2 /dev/mtdblock12 /reserved mount -t yaffs2 /dev/mtdblock13 /mnt mount -t tmpfs nodev /tmp echo 1 > /proc/sys/vm/overcommit_memory echo 8192 > /proc/sys/vm/min_free_kbytes
我们不难知道,load脚本主要是执行一些驱动模块的卸载,加载,还有将/dev下的一些分区用正确的格式挂载到对应的目录下。
至此,我们的分析基本完毕,另外还有app的启动也是用脚本,在进入linux时候启动起来的。
# pwd /etc # cat profile # /etc/profile: system-wide .profile file for the Bourne shells # # set_path_before() { [ -d $1 ] && PATH="$1:$PATH" } PATH="/usr/bin:/usr/sbin:/bin:/sbin" set_path_before /usr/local/sbin set_path_before /usr/local/bin LD_LIBRARY_PATH="/usr/local/lib:/usr/lib" export PATH export LD_LIBRARY_PATH # ANSI COLORS " NORMAL="" RED="" GREEN="" YELLOW="" BLUE="" MAGENTA="" CYAN="" WHITE="" umask 022 if [ -e /tmp/run_flag ]; then echo "app is running already! " exit 0 else touch /tmp/run_flag fi #ifconfig eth1 192.168.5.177 #route add DEFAULT 192.168.5.177 #ifconfig lo 127.0.0.1 #/usr/sbin/udhcpd -S /etc/udhcpd.conf #echo "1" >/proc/sys/net/ipv4/ip_forward #cd /etc #./iptables.sh himm 0x600d1310 0x5F1 ifconfig eth0 up ifconfig eth1 up echo "${GREEN}Welcome to HiLinux.${NORMAL}" cd /home while true do ./appmain killall -9 udhcpc usleep 300000 ./appmain 100 done
/etc/profile 文件是登陆linux时候就运行的,app此时启动了。
# cat /etc/fstab proc /proc proc defaults 0 0sysfs /sys sysfs defaults 0 0tmpfs /dev tmpfs defaults 0 0
相关文章推荐
- PostgreSQL存储引擎源码分析二(原创,不断更新)
- Tomato的init启动流程分析(原创)
- 【原创】动态图像监测开源代码 motion 学习 ----- Motion源码分析(1)
- LR(0)文法分析程序[00原创]
- 如何学习小波分析?——小波分析学习方法及学习资料(原创,若转载请标明出处)
- 【原创】日志表设计一例分析
- [导入][原创]正则分析二级或多级域名的主域名
- asp获取URL参数的几种方法分析总结 原创
- [原创]经过整理的源代码 google map 代码分析之一
- [原创]矩阵键盘原理分析手稿
- 【原创】关于Real6410和OK6410 启动方式分析
- 【原创】一个简单的StreamInsight样例分析:MarketMonitor
- 原创:10086手机木马分析手记(2015)
- [iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之一:OBJ格式分析
- 【原创分析】从技术角度分析陈冠希事件的必然性 推荐
- [原创]桓泽学音频编解码(15):AC3 最终章 多声道处理模块算法分析
- 【原创】互斥锁使用分析
- 【原创】Kmeans算法 优缺点分析
- php常用字符串输出方法分析(echo,print,printf及sprintf) 原创
- 『原创』统计建模与R软件-第三章 数据描述性分析