首页 网络技术 系统安全

Linux启动分析――init进程与app启动

概述

本文通过简要分析init进程源码,梳理其处理流程,重点关注init进程如何启动应用程序,总结启动脚本文件的编写思路

init进程源码分析

init进程是linux内核启动的第一个进程,怎么知道的?从内核源码linux-2.6.xxx/init/main.c代码的kernel_init()函数分析,可以发现,内核会根据uboot传入的参数来启动第一个进程,一般都是init

怎么启动的呢,调用kernel_execve()函数完成的,猜测是从根文件系统的/sbin/init来启动的,linux的任何应用程序都是基于文件系统的,启动应用程序前提是根文件系统已经挂载好了。好,那么根文件系统又是从哪里来的呢,是由busybox这个工具配置编译生成的,所以要分析init源码,要去busybox里找init的源码

源码位置:/busybox/init/init.c,在其中查找main()函数,发现只有init_main(),没有main(),可以猜测busybox是通过一些方法将init进程的入口修改为init_main(),实际上所有busybox的命令工具都是一个到busybox程序的链接,

cd /sbin ls -l init lrwxrwxrwx 1 root 0 14 Nov 16 2016 init -> ../bin/busybox  

可以看到,init进程其实是到busybox的链接,不用管它,知道init进程的入口是init_main()函数就行了

#if DEBUG_SEGV_HANDLER     {         struct sigaction sa;         memset(&sa, 0, sizeof(sa));         sa.sa_sigaction = handle_sigsegv;         sa.sa_flags = SA_SIGINFO;         sigaction(SIGSEGV, &sa, NULL);         ......     } #endif ...... console_init(); set_sane_term(); ...... /* 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? */  

这一段是init进程最开始要做的事情,设置一些信号相关的东西,初始化console,然后设置环境变量,跟启动app似乎没有什么关系,不用管,继续往下看

/* 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判断,注释说如果是single user mode,则走上半段代码,如果不是single user mode,则调用parse_inittab() 函数,因为内核启动init进程没有传入附加参数,所以argv[1]不存在,程序走parse_inittab()

注释还说如果没有定义CONFIG_FEATURE_USE_INITTAB 这个宏,程序会执行一些默认的action,那怎么知道这个宏定义了没有呢,猜测这个宏应该是对busybox配置时的选项,好,怎么查看busybox配置呢,和linux内核配置一样的道理,结合make menuconfig和各级config文件来看

是否定义了宏CONFIG_FEATURE_USE_INITTAB?

在busybox中执行make meunconfig,进入熟悉的配置界面

大概浏览一下,和init有关系的好像有个Init Utilities项,进去

这里面有一项“Support reading an inittab file”,这个配置项是选中的,描述中有“inittab”这个单词,和init源码中说到的parse_inittab()很相似,好,make menuconfig先放到一边,来看看配置文件,打开顶层目录的Config.in,全局搜一下"init",发现只有最下面有:

source init/Config.in 

进入init文件夹,打开其中的Config.in文件,发现配置项

config FEATURE_USE_INITTAB     bool "Support reading an inittab file"     default y     depends on INIT     help       Allow init to read an inittab file when the system boot.  

猜测没错,CONFIG_FEATURE_USE_INITTAB这个宏确实定义了,回到init源码分析,进入parse_inittab()函数。首先看到这个函数前有一大段注释,看看它说什么

/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,  * then parse_inittab() simply adds in some default  * actions (i.e., runs INIT_SCRIPT and then starts a pair  * of "askfirst" shells).  If CONFIG_FEATURE_USE_INITTAB  * _is_ defined, but /etc/inittab is missing, this  * results in the same set of default behaviors.  */  

前面的话和之前的if判断意思差不多,如果定义了XXX这个宏,但是/etc/inittab这个文件没有,也会走默认的action,好,大概猜想一下,parse_inttab()函数好像和要分析的app启动有点关系了,如果定义了XXX宏,就去解析/etc/inittab这个文件,执行里面的东西,如果没有定义XXX宏或者/etc/inittab文件不存在,执行一些默认的东西

好,搞清楚一件事,/etc/inittab这个文件很重要,可能需要自己来创建这个文件,往里面写东西,但是写什么内容呢?目前还不知道。那如果不走/etc/inittab这一条路呢,默认会执行的action又是什么意思?来分析一下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 */         /* Sysinit */         new_init_action(SYSINIT, INIT_SCRIPT, "");         /* 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);         /* 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 */         new_init_action(SHUTDOWN, "swapoff -a", "");         /* Restart init when a QUIT is received */         new_init_action(RESTART, "init", "");         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