我们通常使用Busybox来构建根文件系统的必要的应用程序。Busybox通过传入的参数来决定执行何种操作。当init进程启动时,实际上调用的是Busybox的init_main()函数,下面我们来分析这个函数,看init进程究竟是怎样一个流程。我分析的Busybox源码是1.7.0版本的,其他版本会略有不同。部分代码省略我们只看关键性代码。
首先看init_main函数
01.int init_main(int argc, char **argv); 02.int init_main(int argc, char **argv) 03.{ 04. …………………………….. 05. …………………………….. 06. //初始化控制台 07. console_init(); 08. ……………………………… 09. 10. if (argc > 1 11. && (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1')) 12. ) { 13. new_init_action(RESPAWN, bb_default_login_shell, ""); 14. } else { 15. //因为我们启动的init进程没有任何参数,所有argc==1,执行的是这一句 16. parse_inittab(); 17. } 18. ………………………………………… 19. ………………………………………… 20. run_actions(SYSINIT); //运行inittab配置文件中acthion为SYSINIT的进程 21. run_actions(WAIT); //运行inittab配置文件中action为WAIT的进程 22. 23. 24. run_actions(ONCE); //运行inittba配置文件中action为ONCE的进程 25. ……………………………………………… 26. while (1) { 27. /* 28. 运行inittab配置文件中action为RESPAWN和ASKFIRST的进程,一旦退出则重新启动 29. */ 30. run_actions(RESPAWN); 31. run_actions(ASKFIRST); 32. 33. wpid = wait(NULL); 34. while (wpid > 0) { 35. a->pid = 0; 36. } 37. wpid = waitpid(-1, NULL, WNOHANG); 38. 39. } 40.}
parse_inittab实际上对/etc/inittab文件里面的配置进行解释,如果没有,则设置一些默认设置。
我们先来看看这个inittab这个文件里面的配置格式,这个在busybox文件里面的inittab文件里面有说明
<id>:<runlevels>:<action>:<process>
id表示输出输入设备,这个不需要设置,因为/etc/console已经设为标准输入输出了,如不设置,则从控制台输入输出。
runlevels 这个参数完全忽略
action 运行时机,它表示inittab解释后的运行顺序,它有sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, andshutdown.这个值可选择。
process 就是要启动的进程。
下面来看prase_inittab这个函数
01.static void parse_inittab(void) 02.{ 03.………………………………………………… 04.………………………………………………… 05. 06. /*INITTAB是一个宏 #define INITTAB "/etc/inittab" 07. 可以看得出来它打开了/etc/inittab这个文件*/ 08. 09. file = fopen(INITTAB, "r"); 10. 11. //如果没有这个文件,则调用new_init_action进行一些默认的操作 12. if (file == NULL) { 13. new_init_action(CTRLALTDEL, "reboot", ""); 14. new_init_action(SHUTDOWN, "umount -a -r", ""); 15. if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", ""); 16. new_init_action(RESTART, "init", ""); 17. new_init_action(ASKFIRST, bb_default_login_shell, ""); 18. new_init_action(ASKFIRST, bb_default_login_shell, VC_2); 19. new_init_action(ASKFIRST, bb_default_login_shell, VC_3); 20. new_init_action(ASKFIRST, bb_default_login_shell, VC_4); new_init_action(SYSINIT, INIT_SCRIPT, ""); 21. 22. return; 23. } 24. ………………………………………………… 25.………………………………………………… 26. /*果inittab文件里面有内容就将里面的内容一行一行读出来,然后调用new_init_action进行操作*/ 27. while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) { 28. /* Ok, now process it */ 29. for (a = actions; a->name != 0; a++) { 30. if (strcmp(a->name, action) == 0) { 31. if (*id != '\0') { 32. if (strncmp(id, "/dev/", 5) == 0) 33. id += 5; 34. strcpy(tmpConsole, "/dev/"); 35. safe_strncpy(tmpConsole + 5, id, 36. sizeof(tmpConsole) - 5); 37. id = tmpConsole; 38. } 39. new_init_action(a->action, command, id); 40. break; 41. } 42. } 43. ………………………………………………… 44.………………………………………………… 45. } 46. fclose(file); 47.}
这个new_init_action函数,它实际上是将inittab里面的action相同的操作串成一个链表。
下面我们再来分析init_main执行prase_inittab之后执行的操作
可以看出init_main执行prase_initab对inittab文件里面的配置进行解释之后,会先执行运行时机为SYSINIT的进程,让执行WAIT时机的,接着是ONCE的,然后在一个while(1)函数里面运行RESPAWN和ASKFIRST时机的,一旦这两个时机里面的进程被杀死,就会把他们的pid赋为0,然后跳到while(1)函数的开始处又去启动他们。所有说运行时机为RESPAWN和ASKFIRST的进程永远无法杀死,除非reboot或者shutdown。