Android 启动过程(1)

分 析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系统
html

2.解析/init.rc和/init.%hardware%.rc文件  node

3. 执行 early-init action in the two files parsed in step 2.  linux

4. 设备初始化,例如:在 /dev 下面建立全部设备节点,下载 firmwares.  android

5.初始化属性服务器,Act lly 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. 开启 属性服务。app

8. 执行 early-boot and boot actions in the two files parsed in step 2.  dom

9. 执行 Execute property action in the two files parsed in step 2.  socket

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/req st_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_q _tail);
    drain_action_q ();
    …
    /* 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_q _tail);
    drain_action_q ();
    …
    /* 执行 early-boot and boot actions in the two files parsed in step 2 */
    action_for_each_trigger(”early-boot”, action_add_q _tail);
    action_for_each_trigger(”boot”, action_add_q _tail);
    drain_action_q ();
    /* run all property triggers based on current state of the properties */
    q _all_property_triggers();
    drain_action_q ();
    /* enable property triggers */  
    property_triggers_enabled = 1;   
    …
    for(;;) {
        int nr, timeout = -1;
    …
        drain_action_q ();
        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_q );
*.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(str t 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(str t parse_state *state, int nargs, char **args)
{
    str t 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/req st_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 *val , const char *default_val )
状态设置函数:
Property_service.c (system\core\init)
int property_set(const char *name, const char *val )
Properties.c (system\core\libcutils)
int property_set(const char *key, const char *val )
在终端模式下咱们能够经过执行命令 setprop  
setprop 工具源代码所在文件: Setprop.c (system\core\toolbox)
Getprop.c (system\core\toolbox):        property_get(argv[1], val , default_val );
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, val ) //Init.c (system\core\init)
      q _property_triggers(name, val )
      drain_action_q ()
只要属性一改变就会被触发,而后执行相应的命令:  
例如:
在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_q _tail(act)将其插入到 队列 action_q 尾部。drain_action_q () 从action_list队列中取出队列项 ,而后执行act->commands
列表中的全部命令。
因此从  ./system/core/init/init.c mian()函数的程序片断:
action_for_each_trigger(”early-init”, action_add_q _tail);
drain_action_q ();
action_for_each_trigger(”init”, action_add_q _tail);
drain_action_q ();
action_for_each_trigger(”early-boot”, action_add_q _tail);
action_for_each_trigger(”boot”, action_add_q _tail);
drain_action_q ();
/* run all property triggers based on current state of the properties */
q _all_property_triggers();
drain_action_q ();
能够看出,在解析完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 individ lly.
         */
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}
void service_for_each_class(const char *classname,
                            void (*func)(str t service *svc))
{
    str t listnode *node;
    str t service *svc;
    list_for_each(node, &service_list) {
        svc = node_to_item(node, str t service, slist);
        if (!strcmp(svc->classname, classname)) {
            func(svc);
        }
    }
}
由于在调用 parse_service() 添加服务列表的时候,全部服务 svc->classname 默认取值:”default”,
因此 service_list 中的全部服务将会被执行。
参考文档:
http://blog.chinaunix.net/ /38994/showart_1775465.html
http://blog.chinaunix.net/ /38994/showart_1168440.html
浅析kernel启动的第1个用户进程init如何解读init.rc脚本
http://blog.chinaunix.net/ /38994/showart_1168440.htmlide

相关文章
相关标签/搜索