滴水成冰的造句:Android init 启动过程分析 - gpephone

来源:百度文库 编辑:偶看新闻 时间:2024/04/29 18:54:36
Android init 启动过程分析分析android的启动过程,从内核之上,我们首先应该从文件系统的init开始,因为 init是内核进入文件系统后第一个运行的程序,通常我们可以在linux的命令行中指定内核第一个调用谁,如果没指定那么内核将会到/sbin/, /bin/等目录下查找默认的init,如果没有找到那么就报告出错。
下面是曾经用过的几种开发板的命令行参数:
S3C2410 启动参数:
noinitrdroot=/dev/nfs  nfsroot=192.168.2.56:/nfsroot/rootfs  ip=192.168.2.188:192.168.2.56:192.168.2.56:255.255.255.0::eth0:onconsole=ttySAC0
S3C2440 启动参数:
setenv bootargs console=ttySAC0root=/dev/nfs nfsroot=192.168.2.56:/nfsroot/rootfsip=192.168.2.175:192.168.2.56:192.168.2.201:255.255.255.0::eth0:onmem=64M init=/init         
marvell 310 启动参数:
boot root=/dev/nfsnfsroot=192.168.2.56:/nfsroot/rootfs,rsize=1024,wsize=1024ip=192.168.2.176:192.168.2.201:192.168.2.201:255.255.255.0::eth0:-On console=ttyS2,115200 mem=64M init=/init

init的源代码在文件:./system/core/init/init.c 中,init会一步步完成下面的任务:
1.初始化log系统
 
2.解析/init.rc和/init.%hardware%.rc文件  
 
3. 执行 early-init action in the two filesparsed in step 2.  
 
4. 设备初始化,例如:在 /dev 下面创建所有设备节点,下载 firmwares. 
 
5. 初始化属性服务器,Actually the property system is working as ashare memory. Logically it looks like a registry under Windows system.  
 
6.执行 init action in the two files parsed in step 2.  
 
7. 开启 属性服务。
 
8.执行 early-boot and boot actions in the two files parsed in step 2.  
 
9.执行 Execute property action in the two files parsed in step 2.  
 
10.进入一个无限循环 to wait for device/property set/child process exit events.例如,如果SD卡被插入,init会收到一个设备插入事件,它会为这个设备创建节点。系统中比较重要的进程都是由init来fork的,所以如果他们他谁崩溃了,那么init 将会收到一个 SIGCHLD 信号,把这个信号转化为子进程退出事件, 所以在loop中,init 会操作进程退出事件并且执行*.rc 文件中定义的命令。
例如,在init.rc中,因为有:
service zygote/system/bin/app_process -Xzygote /system/bin --zygote--start-system-server
    socket zygote stream 666
    onrestartwrite /sys/android_power/request_state wake
    onrestart write/sys/power/state on
所以,如果zygote因为启动某些服务导致异常退出后,init将会重新去启动它。

intmain(int argc, char **argv)
{
    ...
   //需要在后面的程序中看打印信息的话,需要屏蔽open_devnull_stdio()函数
   open_devnull_stdio();
    ...
    //初始化log系统
    log_init();
   //解析/init.rc和/init.%hardware%.rc文件
   parse_config_file("/init.rc");
    ...
    snprintf(tmp,sizeof(tmp), "/init.%s.rc", hardware);
    parse_config_file(tmp);
   ...
    //执行 early-init action in the two files parsed in step 2.
   action_for_each_trigger("early-init", action_add_queue_tail);
   drain_action_queue();
    ...
    /* execute all the boot actionsto get us started */
    /* 执行 init action in the two files parsed instep 2 */
    action_for_each_trigger("init",action_add_queue_tail);
    drain_action_queue();
    ...
   /* 执行 early-boot and boot actions in the two files parsed in step 2 */
   action_for_each_trigger("early-boot", action_add_queue_tail);
   action_for_each_trigger("boot", action_add_queue_tail);
   drain_action_queue();

    /* run all property triggers based oncurrent state of the properties */
    queue_all_property_triggers();
   drain_action_queue();

    /* enable property triggers */  
   property_triggers_enabled = 1;    
    ...
    for(;;) {
       int nr, timeout = -1;
    ...
        drain_action_queue();
       restart_processes();

        if (process_needs_restart) {
           timeout = (process_needs_restart - gettime()) * 1000;
            if(timeout < 0)
                timeout = 0;
        }
    ...
        nr = poll(ufds, 3, timeout);
        if (nr <= 0)
           continue;

        if (ufds[2].revents == POLLIN) {
           /* we got a SIGCHLD - reap and restart as needed */
           read(signal_recv_fd, tmp, sizeof(tmp));
            while(!wait_for_one_process(0))
                ;
            continue;
       }

        if (ufds[0].revents == POLLIN)
           handle_device_fd(device_fd);

        if (ufds[1].revents ==POLLIN)
    {
            handle_property_set_fd(property_set_fd);
   }
    }

    return 0;
}


2.解析init.rc脚本
init.rc脚本的具体语法可以参考下面文档
http://www.kandroid.org/android_pdk/bring_up.html
名词解释:
Android初始化語言由四大类声明组成:行为类(Actions),命令类(Commands),服务类(Services),选项类(Options).
初始化语言以行为单位,由以空格间隔的语言符号組成。C风格的反斜杠转义符可以用来插入空白到语言符号。双引号也可以用来防止文本被空格分成多个语言符号。当反斜杠在行末时,作为换行符。
* 以#开始(前面允许空格)的行为注释。
*Actions和Services隐含声明一个新的段落。所有该段落下Commands或Options的声明属于该段落。第一段落前的Commands或Options被忽略。
* Actions和Services拥有唯一的命名。在他们之后声明相同命名的类将被当作错误并忽略。
Actions是一系列命令的命名。Actions拥有一个触发器(trigger)用来決定action何時执行。当一个action在符合触发条件被执行时,如果它还没被加入到待执行队列中的话,則加入到队列最后。
队列中的action依次执行,action中的命令也依次执行。Init在执行命令的中间处理其他活动(设备创建/销毁,property 设置,进程重启)。

Actions的表现形式:
on
 
 
 
重要的数据结构
两个列表,一个队列。
staticlist_declare(service_list);
static list_declare(action_list);
staticlist_declare(action_queue);
*.rc 脚本中所有 service关键字定义的服务将会添加到service_list 列表中。
*.rc 脚本中所有 on     关键开头的项将会被会添加到 action_list 列表中。
每个action列表项都有一个列表,此列表用来保存该段落下的 Commands

脚本解析过程:
parse_config_file("/init.rc")
intparse_config_file(const char *fn)
{
    char *data;
    data =read_file(fn, 0);
    if (!data) return -1;

   parse_config(fn, data);
    DUMP();
    return 0;
}
staticvoid parse_config(const char *fn, char *s)

    ...
    caseT_NEWLINE:
        if (nargs) {
            int kw =lookup_keyword(args[0]);
            if (kw_is(kw, SECTION)) {
               state.parse_line(&state, 0, 0);
               parse_new_section(&state, kw, nargs, args);
            } else {
               state.parse_line(&state, nargs, args);
            }
           nargs = 0;
        }
   ...


parse_config会逐行对脚本进行解析,如果关键字类型为  SECTION ,那么将会执行 parse_new_section()
类型为 SECTION 的关键字有: on 和sevice
关键字类型定义在 Parser.c (system\core\init) 文件中
Parser.c(system\core\init)
#define SECTION 0x01
#define COMMAND 0x02
#defineOPTION  0x04
关键字        属性       
capability,  OPTION,  0, 0)
class,      OPTION,  0, 0)
class_start, COMMAND, 1, do_class_start)
class_stop, COMMAND, 1, do_class_stop)
console,     OPTION,  0, 0)
critical,   OPTION,  0, 0)
disabled,    OPTION,  0, 0)
domainname,  COMMAND,1, do_domainname)
exec,        COMMAND, 1, do_exec)
export,     COMMAND, 2, do_export)
group,       OPTION,  0, 0)
hostname,   COMMAND, 1, do_hostname)
ifup,        COMMAND, 1, do_ifup)
insmod,     COMMAND, 1, do_insmod)
import,      COMMAND, 1, do_import)
keycodes,   OPTION,  0, 0)
mkdir,       COMMAND, 1, do_mkdir)
mount,      COMMAND, 3, do_mount)
on,          SECTION, 0, 0)
oneshot,    OPTION,  0, 0)
onrestart,   OPTION,  0, 0)
restart,     COMMAND,1, do_restart)
service,     SECTION, 0, 0)
setenv,      OPTION, 2, 0)
setkey,      COMMAND, 0, do_setkey)
setprop,     COMMAND, 2,do_setprop)
setrlimit,   COMMAND, 3, do_setrlimit)
socket,     OPTION,  0, 0)
start,       COMMAND, 1, do_start)
stop,       COMMAND, 1, do_stop)
trigger,     COMMAND, 1, do_trigger)
symlink,    COMMAND, 1, do_symlink)
sysclktz,    COMMAND, 1, do_sysclktz)
user,       OPTION,  0, 0)
write,       COMMAND, 2, do_write)
chown,      COMMAND, 2, do_chown)
chmod,       COMMAND, 2, do_chmod)
loglevel,   COMMAND, 1, do_loglevel)
device,      COMMAND, 4, do_device)

parse_new_section()中再分别对 service 或者 on 关键字开头的内容进行解析。
    ...
    case K_service:
       state->context = parse_service(state, nargs, args);
        if(state->context) {
            state->parse_line =parse_line_service;
            return;
        }
       break;
    case K_on:
        state->context =parse_action(state, nargs, args);
        if (state->context) {
           state->parse_line = parse_line_action;
            return;
       }
        break;
    }
    ...

对 on 关键字开头的内容进行解析
staticvoid *parse_action(struct parse_state *state, int nargs, char **args)
{
   ...
    act = calloc(1, sizeof(*act));
    act->name =args[1];
    list_init(&act->commands);
   list_add_tail(&action_list, &act->alist);
    ...
}

对service 关键字开头的内容进行解析
static void *parse_service(struct parse_state*state, int nargs, char **args)
{
    struct service *svc;
   if (nargs < 3) {
        parse_error(state, "services must have aname and a program\n");
        return 0;
    }
    if(!valid_name(args[1])) {
        parse_error(state, "invalid servicename '%s'\n", args[1]);
        return 0;
    }
   //如果服务已经存在service_list列表中将会被忽略
    svc =service_find_by_name(args[1]);
    if (svc) {
       parse_error(state, "ignored duplicate definition of service '%s'\n",args[1]);
        return 0;
    }
   
    nargs -= 2;
   svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
    if (!svc){
        parse_error(state, "out of memory\n");
        return0;
    }
    svc->name = args[1];
    svc->classname ="default";
    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
   svc->args[nargs] = 0;
    svc->nargs = nargs;
   svc->onrestart.name = "onrestart";
   list_init(&svc->onrestart.commands);
    //添加该服务到 service_list列表
    list_add_tail(&service_list, &svc->slist);
   return svc;
}
服务的表现形式:
service [ ]*