您的位置:首页 > 其它

init 分析

2011-10-19 10:02 99 查看
分析android的启动过程,从内核之上,我们首先应该从文件系统的init开始,因为 init 是内核进入文件系统后第一个运行的程序,通常我们可以在linux的命令行中指定内核第一个调用谁,如果没指定那么内核将会到/sbin/,/bin/ 等目录下查找默认的init,如果没有找到那么就报告出错。

下面是曾经用过的几种开发板的命令行参数:

S3C2410 启动参数:

noinitrd

root=/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:on

console=ttySAC0

S3C2440 启动参数:

setenv bootargs console=ttySAC0

root=/dev/nfs nfsroot=192.168.2.56:/nfsroot/rootfs

ip=192.168.2.175:192.168.2.56:192.168.2.201:255.255.255.0::eth0:on

mem=64M init=/init         

marvell 310 启动参数:

boot root=/dev/nfs

nfsroot=192.168.2.56:/nfsroot/rootfs,rsize=1024,wsize=1024

ip=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 files parsed in step 2.  
4. 设备初始化,例如:在 /dev 下面创建所有设备节点,下载 firmwares.  
5.初始化属性服务器,Actually the property system is working as a share 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

    onrestart write /sys/android_power/request_state wake

    onrestart write /sys/power/state on

所以,如果zygote因为启动某些服务导致异常退出后,init将会重新去启动它。

int main(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 actions to get us started */

    /* 执行 init action in the two files parsed in step 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 on current 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

  

  

重要的数据结构两个列表,一个队列。

static list_declare(service_list);

static list_declare(action_list);

static list_declare(action_queue);

*.rc 脚本中所有 service关键字定义的服务将会添加到 service_list 列表中。

*.rc 脚本中所有 on     关键开头的项将会被会添加到 action_list 列表中。

每个action列表项都有一个列表,此列表用来保存该段落下的 Commands脚本解析过程:

parse_config_file(”/init.rc”)

int parse_config_file(const char *fn)

{

    char *data;

    data = read_file(fn, 0);

    if (!data) return -1;

    parse_config(fn, data);

    DUMP();

    return 0;

}

static void parse_config(const char *fn, char *s)



    …

    case T_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 0×01

#define COMMAND 0×02

#define OPTION  0×04

关键字        属性      

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 关键字开头的内容进行解析

static void *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 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   [  ]*



申请一个service结构体,然后挂接到service_list链表上,name 为服务的名称 pathname 为执行的命令 argument 为命令的参数。之后的 option 用来控制这个service结构体的属性,parse_line_service 会对 service关键字后的 内容进行解析并填充到 service 结构中 ,当遇到下一个service或者on关键字的时候此service选项解析结束。

例如:

service zygote /system/bin/app_process -Xzygote /system/bin –zygote –start-system-server

    socket zygote stream 666

    onrestart write /sys/android_power/request_state wake

服务名称为:                           zygote

启动该服务执行的命令:                 /system/bin/app_process

命令的参数:                           -Xzygote /system/bin –zygote –start-system-server

socket zygote stream 666: 创建一个名为:/dev/socket/zygote 的 socket ,类型为:stream

当*.rc 文件解析完成以后:

action_list 列表项目如下:

on init

on boot

on property:ro.kernel.qemu=1

on property:persist.service.adb.enable=1

on property:persist.service.adb.enable=0

init.marvell.rc 文件

on early-init

on init

on early-boot

on boot

service_list 列表中的项有:

service console

service adbd

service servicemanager

service mountd

service debuggerd

service ril-daemon

service zygote

service media

service bootsound

service dbus

service hcid

service hfag

service hsag

service installd

service flash_recovery

状态服务器相关:

在init.c 的main函数中启动状态服务器。

property_set_fd = start_property_service();

状态读取函数:

Property_service.c (system\core\init)

const char* property_get(const char *name)

Properties.c (system\core\libcutils)

int property_get(const char *key, char *value, const char *default_value)

状态设置函数:

Property_service.c (system\core\init)

int property_set(const char *name, const char *value)

Properties.c (system\core\libcutils)

int property_set(const char *key, const char *value)

在终端模式下我们可以通过执行命令 setprop  

setprop 工具源代码所在文件: Setprop.c (system\core\toolbox)

Getprop.c (system\core\toolbox):        property_get(argv[1], value, default_value);

Property_service.c (system\core\init)

中定义的状态读取和设置函数仅供init进程调用,

handle_property_set_fd(property_set_fd);

  property_set()   //Property_service.c (system\core\init)

    property_changed(name, value) //Init.c (system\core\init)

      queue_property_triggers(name, value)

      drain_action_queue()

只要属性一改变就会被触发,然后执行相应的命令:  

例如:

在init.rc 文件中有

on property:persist.service.adb.enable=1

  start adbd

on property:persist.service.adb.enable=0

  stop adbd

所以如果在终端下输入:

setprop property:persist.service.adb.enable 1或者0

那么将会开启或者关闭adbd 程序。

执行action_list 中的命令:

从action_list 中取出 act->name 为 early-init 的列表项,再调用 action_add_queue_tail(act)将其插入到 队列 action_queue 尾部。drain_action_queue() 从action_list队列中取出队列项 ,然后执行act->commands

列表中的所有命令。

所以从  ./system/core/init/init.c mian()函数的程序片段:

action_for_each_trigger(”early-init”, action_add_queue_tail);

drain_action_queue();

action_for_each_trigger(”init”, action_add_queue_tail);

drain_action_queue();

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 on current state of the properties */

queue_all_property_triggers();

drain_action_queue();

可以看出,在解析完init.rc init.marvell.rc 文件后,action 命令执行顺序为:

执行act->name 为 early-init,act->commands列表中的所有命令

执行act->name 为 init,            act->commands列表中的所有命令

执行act->name 为 early-boot,act->commands列表中的所有命令

执行act->name 为 boot,            act->commands列表中的所有命令

关键的几个命令:

class_start default   启动所有service 关键字定义的服务。

class_start 在act->name为boot的 act->commands列表中,所以当 class_start 被触发后,实际上调用的是函数 do_class_start()

int do_class_start(int nargs, char **args)

{

        /* Starting a class does not start services

         * which are explicitly disabled.  They must

         * be started individually.

         */

    service_for_each_class(args[1], service_start_if_not_disabled);

    return 0;

}

void service_for_each_class(const char *classname,

                            void (*func)(struct service *svc))

{

    struct listnode *node;

    struct service *svc;

    list_for_each(node, &service_list) {

        svc = node_to_item(node, struct service, slist);

        if (!strcmp(svc->classname, classname)) {

            func(svc);

        }

    }

}

因为在调用 parse_service() 添加服务列表的时候,所有服务 svc->classname 默认取值:”default”,

所以 service_list 中的所有服务将会被执行。

参考文档:
http://blog.chinaunix.net/u1/38994/showart_1775465.html
http://blog.chinaunix.net/u1/38994/showart_1168440.html

浅析kernel启动的第1个用户进程init如何解读init.rc脚本
http://blog.chinaunix.net/u1/38994/showart_1168440.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息