代码改变世界

《深入理解Android(卷1)》笔记 2.第三章 深入理解init

2012-11-30 13:13  ...平..淡...  阅读(1419)  评论(3编辑  收藏  举报

重拾书本...

作者是基于2.2源码分析的,由于我参照的是2.3,有些方法略有不同,我按照2.3的来总结,不过会在不同的地方标记下。

第三章 深入理解init

知识点1:init概述

init是一个进程,而且是Linux系统中用户空间的第一个进程。主要工作概述为:

(1)init负责创建系统中的几个关键进程,包括zygote。

(2)init提供property service(属性服务)来管理Android系统的属性。

这一章是从 init如何创建zygote init的属性服务如何工作 两个方面分析init。

 

知识点2:init工作流程(精简)

(1)解析两个配置文件 (init.rc 和 init.hardware名.rc)。(init.rc 是系统配置文件,init.hardware名.rc 是与硬件平台相关的配置文件)

(2)执行各个阶段的动作。(总共4个阶段(early-init,init,early-boot,boot),创建zygote的工作就在某个阶段)

(3)调用property_init初始化属性相关的资源,并且通过property_start_service启动属性服务。

(4)init进入一个无限循环,并且等待一些事情的发生。(重点关注init如何处理来自socket和来自属性服务器的相关事情)

贴出init.c中的main方法源码

init.c/main
  1 int main(int argc, char **argv)
  2 {
  3     int fd_count = 0;
  4     struct pollfd ufds[4];
  5     char *tmpdev;
  6     char* debuggable;
  7     char tmp[32];
  8     int property_set_fd_init = 0;
  9     int signal_fd_init = 0;
 10     int keychord_fd_init = 0;
 11 
 12     if (!strcmp(basename(argv[0]), "ueventd"))
 13         return ueventd_main(argc, argv);
 14 
 15     /* clear the umask */
 16     umask(0);
 17 
 18         /* Get the basic filesystem setup we need put
 19          * together in the initramdisk on / and then we'll
 20          * let the rc file figure out the rest.
 21          */
 22     mkdir("/dev", 0755);
 23     mkdir("/proc", 0755);
 24     mkdir("/sys", 0755);
 25 
 26     mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
 27     mkdir("/dev/pts", 0755);
 28     mkdir("/dev/socket", 0755);
 29     mount("devpts", "/dev/pts", "devpts", 0, NULL);
 30     mount("proc", "/proc", "proc", 0, NULL);
 31     mount("sysfs", "/sys", "sysfs", 0, NULL);
 32 
 33         /* We must have some place other than / to create the
 34          * device nodes for kmsg and null, otherwise we won't
 35          * be able to remount / read-only later on.
 36          * Now that tmpfs is mounted on /dev, we can actually
 37          * talk to the outside world.
 38          */
 39     open_devnull_stdio();
 40     log_init();
 41 
 42     INFO("reading config file\n");
 43     init_parse_config_file("/init.rc");
 44 
 45     /* pull the kernel commandline and ramdisk properties file in */
 46     import_kernel_cmdline(0);
 47 
 48     get_hardware_name(hardware, &revision);
 49     snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
 50     init_parse_config_file(tmp);
 51 
 52     action_for_each_trigger("early-init", action_add_queue_tail);
 53 
 54     queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
 55     queue_builtin_action(property_init_action, "property_init");
 56     queue_builtin_action(keychord_init_action, "keychord_init");
 57     queue_builtin_action(console_init_action, "console_init");
 58     queue_builtin_action(set_init_properties_action, "set_init_properties");
 59 
 60         /* execute all the boot actions to get us started */
 61     action_for_each_trigger("init", action_add_queue_tail);
 62     action_for_each_trigger("early-fs", action_add_queue_tail);
 63     action_for_each_trigger("fs", action_add_queue_tail);
 64     action_for_each_trigger("post-fs", action_add_queue_tail);
 65 
 66     queue_builtin_action(property_service_init_action, "property_service_init");
 67     queue_builtin_action(signal_init_action, "signal_init");
 68     queue_builtin_action(check_startup_action, "check_startup");
 69 
 70     /* execute all the boot actions to get us started */
 71     action_for_each_trigger("early-boot", action_add_queue_tail);
 72     action_for_each_trigger("boot", action_add_queue_tail);
 73 
 74         /* run all property triggers based on current state of the properties */
 75     queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers");
 76 
 77 
 78 #if BOOTCHART
 79     queue_builtin_action(bootchart_init_action, "bootchart_init");
 80 #endif
 81 
 82     for(;;) {
 83         int nr, i, timeout = -1;
 84 
 85         execute_one_command();
 86         restart_processes();
 87 
 88         if (!property_set_fd_init && get_property_set_fd() > 0) {
 89             ufds[fd_count].fd = get_property_set_fd();
 90             ufds[fd_count].events = POLLIN;
 91             ufds[fd_count].revents = 0;
 92             fd_count++;
 93             property_set_fd_init = 1;
 94         }
 95         if (!signal_fd_init && get_signal_fd() > 0) {
 96             ufds[fd_count].fd = get_signal_fd();
 97             ufds[fd_count].events = POLLIN;
 98             ufds[fd_count].revents = 0;
 99             fd_count++;
100             signal_fd_init = 1;
101         }
102         if (!keychord_fd_init && get_keychord_fd() > 0) {
103             ufds[fd_count].fd = get_keychord_fd();
104             ufds[fd_count].events = POLLIN;
105             ufds[fd_count].revents = 0;
106             fd_count++;
107             keychord_fd_init = 1;
108         }
109 
110         if (process_needs_restart) {
111             timeout = (process_needs_restart - gettime()) * 1000;
112             if (timeout < 0)
113                 timeout = 0;
114         }
115 
116         if (!action_queue_empty() || cur_action)
117             timeout = 0;
118 
119 #if BOOTCHART
120         if (bootchart_count > 0) {
121             if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
122                 timeout = BOOTCHART_POLLING_MS;
123             if (bootchart_step() < 0 || --bootchart_count == 0) {
124                 bootchart_finish();
125                 bootchart_count = 0;
126             }
127         }
128 #endif
129 
130         nr = poll(ufds, fd_count, timeout);
131         if (nr <= 0)
132             continue;
133 
134         for (i = 0; i < fd_count; i++) {
135             if (ufds[i].revents == POLLIN) {
136                 if (ufds[i].fd == get_property_set_fd())
137                     handle_property_set_fd();
138                 else if (ufds[i].fd == get_keychord_fd())
139                     handle_keychord();
140                 else if (ufds[i].fd == get_signal_fd())
141                     handle_signal();
142             }
143         }
144     }
145 
146     return 0;
147 }

 

知识点3:解析配置文件

1.init首先调用init_parse_config_file()方法(2.2上是parse_config_file())来进行解析。[-->init_parser.c]  (2.2中是在parser.c)

int init_parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0); //读取配置文件的内容
    if (!data) return -1;

    parse_config(fn, data);  //调用parse_config做真正的解析
    DUMP();
    return 0;
}

 

2.该方法中会调用首先调用read_file读取配置文件内容,然后调用parse_config进行解析。[-->init_parser.c]

static void parse_config(const char *fn, char *s)
{
    struct parse_state state;
    char *args[INIT_PARSER_MAXARGS];
    int nargs;

    nargs = 0;
    state.filename = fn;
    state.line = 0;
    state.ptr = s;
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op;  //设置解析函数,不同的内容用不同的解析函数
    for (;;) {
        switch (next_token(&state)) {
        case T_EOF:
            state.parse_line(&state, 0, 0);
            return;
        case T_NEWLINE: //新的一行
            state.line++;
            if (nargs) {
                int kw = lookup_keyword(args[0]); //得到关键字的类型,比如service,则返回K_service
                if (kw_is(kw, SECTION)) {  //判断关键字类型是不是SECTION,只有service和on满足
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);  //解析这个SECTION
                } else {
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }
}

分析:parse_config方法中的无限for循环,主要对init.rc的内容进行解析,以一行一行进行读取,每读取完一行内容换行到下一行时,使用lookup_keyword分析已经读取的一行的第一个参数。在case T_NEWLINE中首先找到配置文件的一个section(参考a),然后调用parse_new_section方法,它会针对不同的section 使用不同的解析函数来解析。section和init.rc文件的组织结构有关。

void parse_new_section(struct parse_state *state, int kw,
                       int nargs, char **args)
{
    printf("[ %s %s ]\n", args[0],
           nargs > 1 ? args[1] : "");
    switch(kw) {
    case K_service:   //如果symbol是service,调用parse_service和parse_line_service方法
        state->context = parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_service;
            return;
        }
        break;
    case K_on:    //如果symbol是on,调用parse_action和parse_line_action方法
        state->context = parse_action(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_action;
            return;
        }
        break;
    }
    state->parse_line = parse_line_no_op;
}

(a)根据keywords.h的定义,当symbol为on或service时,关键字才被认为是section。(关键字中最重要的是symbolflags,请阅读(3) )

keywords.h中定义如下:

KEYWORD(on,  SECTION, 0, 0)
KEYWORD(service,  SECTION, 0, 0)

下面来分析关键字。

 

3.关键字定义[--->keywords.h] keywords.h中定义了init中使用的关键字。

keywords.h
 #ifndef KEYWORD
 //声明一个函数,这些函数就是前面所说的Action的执行函数。
 int do_chroot(int nargs, char **args);
 int do_chdir(int nargs, char **args);
 int do_class_start(int nargs, char **args);
 int do_class_stop(int nargs, char **args);
 int do_class_reset(int nargs, char **args);
 int do_domainname(int nargs, char **args);
 int do_exec(int nargs, char **args);
 int do_export(int nargs, char **args);
 int do_hostname(int nargs, char **args);
 int do_ifup(int nargs, char **args);
 int do_insmod(int nargs, char **args);
 int do_mkdir(int nargs, char **args);
 int do_mount(int nargs, char **args);
 int do_restart(int nargs, char **args);
 int do_rm(int nargs, char **args);
 int do_rmdir(int nargs, char **args);
 int do_setkey(int nargs, char **args);
 int do_setprop(int nargs, char **args);
 int do_setrlimit(int nargs, char **args);
 int do_start(int nargs, char **args);
 int do_stop(int nargs, char **args);
 int do_trigger(int nargs, char **args);
 int do_symlink(int nargs, char **args);
 int do_sysclktz(int nargs, char **args);
 int do_write(int nargs, char **args);
 int do_copy(int nargs, char **args);
 int do_chown(int nargs, char **args);
 int do_chmod(int nargs, char **args);
 int do_loglevel(int nargs, char **args);
 int do_load_persist_props(int nargs, char **args);
 int do_wait(int nargs, char **args);
 #define __MAKE_KEYWORD_ENUM__  //定义一个宏
 /*
   定义KEYWORD宏,虽然有四个参数,但这里只用第一个,其中K——##symbol中的##表示连接的意思,即最后得到的值为K——symbol。symbol其实就是init.rc中的关键字。
 */
 #define KEYWORD(symbol, flags, nargs, func) K_##symbol,
 enum {  //定义一个枚举,这个枚举定义了各个关键字的枚举值。
     K_UNKNOWN,
 #endif
     KEYWORD(capability,  OPTION,  0, 0)
     KEYWORD(chdir,       COMMAND, 1, do_chdir)
     KEYWORD(chroot,      COMMAND, 1, do_chroot)
     KEYWORD(class,       OPTION,  0, 0)
     KEYWORD(class_start, COMMAND, 1, do_class_start)
     KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
     KEYWORD(class_reset, COMMAND, 1, do_class_reset)
     KEYWORD(console,     OPTION,  0, 0)
     KEYWORD(critical,    OPTION,  0, 0)
     KEYWORD(disabled,    OPTION,  0, 0)
     KEYWORD(domainname,  COMMAND, 1, do_domainname)
     KEYWORD(exec,        COMMAND, 1, do_exec)
     KEYWORD(export,      COMMAND, 2, do_export)
     KEYWORD(group,       OPTION,  0, 0)
     KEYWORD(hostname,    COMMAND, 1, do_hostname)
     KEYWORD(ifup,        COMMAND, 1, do_ifup)
     KEYWORD(insmod,      COMMAND, 1, do_insmod)
     KEYWORD(import,      SECTION, 1, 0)
     KEYWORD(keycodes,    OPTION,  0, 0)
     KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
     KEYWORD(mount,       COMMAND, 3, do_mount)
     KEYWORD(on,          SECTION, 0, 0)
     KEYWORD(oneshot,     OPTION,  0, 0)
     KEYWORD(onrestart,   OPTION,  0, 0)
     KEYWORD(restart,     COMMAND, 1, do_restart)
     KEYWORD(rm,          COMMAND, 1, do_rm)
     KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
     KEYWORD(service,     SECTION, 0, 0)
     KEYWORD(setenv,      OPTION,  2, 0)
     KEYWORD(setkey,      COMMAND, 0, do_setkey)
     KEYWORD(setprop,     COMMAND, 2, do_setprop)
     KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
     KEYWORD(socket,      OPTION,  0, 0)
     KEYWORD(start,       COMMAND, 1, do_start)
     KEYWORD(stop,        COMMAND, 1, do_stop)
     KEYWORD(trigger,     COMMAND, 1, do_trigger)
     KEYWORD(symlink,     COMMAND, 1, do_symlink)
     KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)
     KEYWORD(user,        OPTION,  0, 0)
     KEYWORD(wait,        COMMAND, 1, do_wait)
     KEYWORD(write,       COMMAND, 2, do_write)
     KEYWORD(copy,        COMMAND, 2, do_copy)
     KEYWORD(chown,       COMMAND, 2, do_chown)
     KEYWORD(chmod,       COMMAND, 2, do_chmod)
     KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
     KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
     KEYWORD(ioprio,      OPTION,  0, 0)
 #ifdef __MAKE_KEYWORD_ENUM__
     KEYWORD_COUNT,
 };
 #undef __MAKE_KEYWORD_ENUM__
 #undef KEYWORD //取消KEYWORD宏的定义
 #endif

 

keywords.h的使用,主要是在 [--->init_parser.c]中总共2次包含了keywords.h头文件。

//第一次包含keywords.h,根据keywords.h的代码,首先会得到一个枚举定义。
#include "keywords.h"
/*
  重新定义KEYWORD宏,这回四个参数全用上了,看起来好像是一个结构体。其中#symbol表示一个字符串,其值为“symbol”
*/
#define KEYWORD(symbol, flags, nargs, func) \
    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
/*
  定义一个结构体keyword_info数组,它用来描述关键字的一些属性,请注意里面的注释内容。
*/
struct {
    const char *name;  //关键字的名称
    int (*func)(int nargs, char **args); //对应关键字的处理函数
    unsigned char nargs;  //参数个数,每个关键字的参数个数是固定的
    unsigned char flags;  //关键字的属性:COMMAND、OPTION、SECTION
} keyword_info[KEYWORD_COUNT] = {
    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
/*
  第二次包含keywords.h,由于已经重新定义了KEYWORD宏,所以以前那些作为枚举值的关键字现在变成了keyword_info数组的索引了。
*/
#include "keywords.h"
};
#undef KEYWORD

#define kw_is(kw, type) (keyword_info[kw].flags & (type))
#define kw_name(kw) (keyword_info[kw].name)
#define kw_func(kw) (keyword_info[kw].func)
#define kw_nargs(kw) (keyword_info[kw].nargs)

主要做了两件事:

(1)第一次包含keywords.h时,它声明了一些诸如do_class_start的函数,另外还定义了一个枚举,枚举值为K_class,K_mkdir等关键字。

(2)第二次包含keywords.h后,得到了一个keyword_info结构体数组,这个keyword_info结构体数组以前面定义的枚举值为索引,存储对应的关键字信息,这些信息包括关键字名称、处理函数、处理函数的参数个数,以及属性。

 

4.解析init.rc

init.rc
on early-init
    start ueventd

on init

sysclktz 0

loglevel 3

# setup the global environment
    export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
    export LD_LIBRARY_PATH /vendor/lib:/system/lib
    export ANDROID_BOOTLOGO 1
    export ANDROID_ROOT /system
    export ANDROID_ASSETS /system/app
    export ANDROID_DATA /data
    export EXTERNAL_STORAGE /mnt/sdcard
    export ASEC_MOUNTPOINT /mnt/asec
    export LOOP_MOUNTPOINT /mnt/obb
    export BOOTCLASSPATH /system/framework/core.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/core-junit.jar
    export TMPDIR /data/local/tmp

# Backward compatibility
    symlink /system/etc /etc
    symlink /sys/kernel/debug /d

# Right now vendor lives on the same filesystem as system,
# but someday that may change.
    symlink /system/vendor /vendor

# create mountpoints
    mkdir /mnt 0775 root system
    mkdir /mnt/sdcard 0000 system system

# Create cgroup mount point for cpu accounting
    mkdir /acct
    mount cgroup none /acct cpuacct
    mkdir /acct/uid

# Backwards Compat - XXX: Going away in G*
    symlink /mnt/sdcard /sdcard

    mkdir /system
    mkdir /data 0771 system system
    mkdir /cache 0770 system cache
    mkdir /config 0500 root root

    # Directory for putting things only root should see.
    mkdir /mnt/secure 0700 root root

    # Directory for staging bindmounts
    mkdir /mnt/secure/staging 0700 root root

    # Directory-target for where the secure container
    # imagefile directory will be bind-mounted
    mkdir /mnt/secure/asec  0700 root root

    # Secure container public mount points.
    mkdir /mnt/asec  0700 root system
    mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000

    # Filesystem image public mount points.
    mkdir /mnt/obb 0700 root system
    mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000

    write /proc/sys/kernel/panic_on_oops 1
    write /proc/sys/kernel/hung_task_timeout_secs 0
    write /proc/cpu/alignment 4
    write /proc/sys/kernel/sched_latency_ns 10000000
    write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
    write /proc/sys/kernel/sched_compat_yield 1
    write /proc/sys/kernel/sched_child_runs_first 0

# XXX for development only
    write /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq 600000
    write /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq 600000

# Create cgroup mount points for process groups
    mkdir /dev/cpuctl
    mount cgroup none /dev/cpuctl cpu
    chown system system /dev/cpuctl
    chown system system /dev/cpuctl/tasks
    chmod 0777 /dev/cpuctl/tasks
    write /dev/cpuctl/cpu.shares 1024

    mkdir /dev/cpuctl/fg_boost
    chown system system /dev/cpuctl/fg_boost/tasks
    chmod 0777 /dev/cpuctl/fg_boost/tasks
    write /dev/cpuctl/fg_boost/cpu.shares 1024

    mkdir /dev/cpuctl/bg_non_interactive
    chown system system /dev/cpuctl/bg_non_interactive/tasks
    chmod 0777 /dev/cpuctl/bg_non_interactive/tasks
    # 5.0 %
    write /dev/cpuctl/bg_non_interactive/cpu.shares 52

on fs
# mount mtd partitions
    # Mount /system rw first to give the filesystem a chance to save a checkpoint
    mount yaffs2 mtd@system /system
    mount yaffs2 mtd@system /system ro remount
    mount yaffs2 mtd@userdata /data nosuid nodev
    mount yaffs2 mtd@persist /persist nosuid nodev
    mount yaffs2 mtd@cache /cache nosuid nodev
    mount yaffs2 mtd@persist /persist nosuid nodev
#if we have a fat.img, bind it to loop7
    exec /system/xbin/fatimgcheck
    losetup /dev/block/loop7 /data/fat.img

on emmc-fs
# mount mmc partitions
    wait /dev/block/mmcblk0p12
    mount ext4 /dev/block/mmcblk0p12 /system ro barrier=1
    wait /dev/block/mmcblk0p13
    exec /system/bin/e2fsck -p /dev/block/mmcblk0p13
    mount ext4 /dev/block/mmcblk0p13 /data nosuid nodev barrier=1 noauto_da_alloc,noinit_itable,nodelalloc
    mount ext4 /dev/block/mmcblk0p14 /persist nosuid nodev barrier=1
    mount ext4 /dev/block/mmcblk0p15 /cache nosuid nodev barrier=1

on post-fs
    # once everything is setup, no need to modify /
    mount rootfs rootfs / ro remount

    # We chown/chmod /data again so because mount is run as root + defaults
    chown system system /data
    chmod 0771 /data

    # Mounting of persist is moved to 'on emmc-fs' and 'on fs' sections
    # We chown/chmod /persist again so because mount is run as root + defaults
    chown system system /persist
    chmod 0771 /persist

    # Create dump dir and collect dumps.
    # Do this before we mount cache so eventually we can use cache for
    # storing dumps on platforms which do not have a dedicated dump partition.
   
    mkdir /data/dontpanic
    chown root log /data/dontpanic
    chmod 0750 /data/dontpanic

    # Collect apanic data, free resources and re-arm trigger
    copy /proc/apanic_console /data/dontpanic/apanic_console
    chown root log /data/dontpanic/apanic_console
    chmod 0640 /data/dontpanic/apanic_console

    copy /proc/apanic_threads /data/dontpanic/apanic_threads
    chown root log /data/dontpanic/apanic_threads
    chmod 0640 /data/dontpanic/apanic_threads

    write /proc/apanic_console 1

    # Same reason as /data above
    chown system cache /cache
    chmod 0770 /cache

    # This may have been created by the recovery system with odd permissions
    chown system cache /cache/recovery
    chmod 0770 /cache/recovery

    #change permissions on vmallocinfo so we can grab it from bugreports
    chown root log /proc/vmallocinfo
    chmod 0440 /proc/vmallocinfo

    #change permissions on kmsg & sysrq-trigger so bugreports can grab kthread stacks
    chown root system /proc/kmsg
    chmod 0440 /proc/kmsg
    chown root system /proc/sysrq-trigger
    chmod 0220 /proc/sysrq-trigger

# create basic filesystem structure
    mkdir /data/misc 01771 system misc
    mkdir /data/misc/bluetoothd 0770 bluetooth bluetooth
    mkdir /data/misc/bluetooth 0770 system system
    mkdir /data/misc/keystore 0700 keystore keystore
    mkdir /data/misc/vpn 0770 system system
    mkdir /data/misc/systemkeys 0700 system system
    mkdir /data/misc/vpn/profiles 0770 system system
    # give system access to wpa_supplicant.conf for backup and restore
    mkdir /data/misc/wifi 0770 wifi wifi
    chmod 0770 /data/misc/wifi
    chmod 0660 /data/misc/wifi/wpa_supplicant.conf
    chown wifi root /data/misc/wifi/wpa_supplicant.conf
    mkdir /data/local 0771 shell shell
    mkdir /data/local/tmp 0771 shell shell
    mkdir /data/data 0771 system system
    mkdir /data/app-private 0771 system system
    mkdir /data/app 0771 system system
    mkdir /data/property 0700 root root

    # create dalvik-cache and double-check the perms
    mkdir /data/dalvik-cache 0771 system system
    chown system system /data/dalvik-cache
    chmod 0771 /data/dalvik-cache

    # create the lost+found directories, so as to enforce our permissions
    mkdir /data/lost+found 0770
    mkdir /cache/lost+found 0770

    # double check the perms, in case lost+found already exists, and set owner
    chown root root /data/lost+found
    chmod 0770 /data/lost+found
    chown root root /cache/lost+found
    chmod 0770 /cache/lost+found

on boot
# basic network init
    ifup lo
    hostname localhost
    domainname localdomain

# set RLIMIT_NICE to allow priorities from 19 to -20
    setrlimit 13 40 40

# Define the oom_adj values for the classes of processes that can be
# killed by the kernel.  These are used in ActivityManagerService.
    setprop ro.FOREGROUND_APP_ADJ 0
    setprop ro.VISIBLE_APP_ADJ 1
    setprop ro.PERCEPTIBLE_APP_ADJ 2
    setprop ro.HEAVY_WEIGHT_APP_ADJ 3
    setprop ro.SECONDARY_SERVER_ADJ 4
    setprop ro.BACKUP_APP_ADJ 5
    setprop ro.HOME_APP_ADJ 6
    setprop ro.HIDDEN_APP_MIN_ADJ 7
    setprop ro.EMPTY_APP_ADJ 15

# Define the memory thresholds at which the above process classes will
# be killed.  These numbers are in pages (4k).
    setprop ro.FOREGROUND_APP_MEM 2048
    setprop ro.VISIBLE_APP_MEM 3072
    setprop ro.PERCEPTIBLE_APP_MEM 4096
    setprop ro.HEAVY_WEIGHT_APP_MEM 4096
    setprop ro.SECONDARY_SERVER_MEM 6144
    setprop ro.BACKUP_APP_MEM 6144
    setprop ro.HOME_APP_MEM 6144
    setprop ro.HIDDEN_APP_MEM 7168
    setprop ro.EMPTY_APP_MEM 8192

# Write value must be consistent with the above properties.
# Note that the driver only supports 6 slots, so we have combined some of
# the classes into the same memory level; the associated processes of higher
# classes will still be killed first.
    write /sys/module/lowmemorykiller/parameters/adj 0,1,2,4,7,15

    write /proc/sys/vm/overcommit_memory 1
    write /proc/sys/vm/min_free_order_shift 4
    write /sys/module/lowmemorykiller/parameters/minfree 2048,3072,4096,6144,7168,8192

    # Set init its forked children's oom_adj.
    write /proc/1/oom_adj -16

    # Tweak background writeout
    write /proc/sys/vm/dirty_expire_centisecs 200
    write /proc/sys/vm/dirty_background_ratio  5

    # Permissions for System Server and daemons.
    chown radio system /sys/android_power/state
    chown radio system /sys/android_power/request_state
    chown radio system /sys/android_power/acquire_full_wake_lock
    chown radio system /sys/android_power/acquire_partial_wake_lock
    chown radio system /sys/android_power/release_wake_lock
    chown radio system /sys/power/state
    chown radio system /sys/power/wake_lock
    chown radio system /sys/power/wake_unlock
    chmod 0660 /sys/power/state
    chmod 0660 /sys/power/wake_lock
    chmod 0660 /sys/power/wake_unlock
    chown system system /sys/class/timed_output/vibrator/enable
    chown system system /sys/class/leds/keyboard-backlight/brightness
    chown system system /sys/class/leds/lcd-backlight/brightness
    chown system system /sys/class/leds/button-backlight/brightness
    chown system system /sys/class/leds/jogball-backlight/brightness
    chown system system /sys/class/leds/red/brightness
    chown system system /sys/class/leds/green/brightness
    chown system system /sys/class/leds/blue/brightness
    chown system system /sys/class/leds/red/device/grpfreq
    chown system system /sys/class/leds/red/device/grppwm
    chown system system /sys/class/leds/red/device/blink
    chown system system /sys/class/leds/red/brightness
    chown system system /sys/class/leds/green/brightness
    chown system system /sys/class/leds/blue/brightness
    chown system system /sys/class/leds/red/device/grpfreq
    chown system system /sys/class/leds/red/device/grppwm
    chown system system /sys/class/leds/red/device/blink
    chown system system /sys/class/timed_output/vibrator/enable
    chown system system /sys/module/sco/parameters/disable_esco
    chown system system /sys/kernel/ipv4/tcp_wmem_min
    chown system system /sys/kernel/ipv4/tcp_wmem_def
    chown system system /sys/kernel/ipv4/tcp_wmem_max
    chown system system /sys/kernel/ipv4/tcp_rmem_min
    chown system system /sys/kernel/ipv4/tcp_rmem_def
    chown system system /sys/kernel/ipv4/tcp_rmem_max
    chown root radio /proc/cmdline

# QMUD / QMI keeps its sockets in here
    mkdir /data/radio
    chmod 0770 /data/radio
    chown radio /data/radio

# Define TCP buffer sizes for various networks
#   ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax,
    setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208
    setprop net.tcp.buffersize.wifi    4095,87380,110208,4096,16384,110208
    setprop net.tcp.buffersize.umts    4094,87380,110208,4096,16384,110208
    setprop net.tcp.buffersize.edge    4093,26280,35040,4096,16384,35040
    setprop net.tcp.buffersize.gprs    4092,8760,11680,4096,8760,11680

    class_start default

## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
    critical

service console /system/bin/sh
    console
    disabled
    user shell
    group log

on property:ro.secure=0
    start console

# adbd is controlled by the persist.service.adb.enable system property
service adbd /sbin/adbd
    disabled

# adbd on at boot in emulator
on property:ro.kernel.qemu=1
    start adbd

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

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

service servicemanager /system/bin/servicemanager
    user system
    critical
#    onrestart restart zygote
    onrestart restart media

service vold /system/bin/vold
    socket vold stream 0660 root mount
    ioprio be 2

service netd /system/bin/netd
    socket netd stream 0660 root system

service debuggerd /system/bin/debuggerd

#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
#    onrestart restart media
#    onrestart restart netd

# .appcache=text/cache-manifest,.mp4=video/mp4
service httpd /system/bin/busybox httpd -f -p 127.0.0.1:7777 -h /
    user root
    group inet cache misc net_admin net_raw log

service ril-daemon-b2g /system/bin/rild
# FIXME: this shouldn't be 0666, but rilproxy checks for that explicitly
    socket rild stream 666 root radio
    socket rild-debug stream 660 radio system
    user root
    group radio cache inet misc audio sdcard_rw qcom_oncrpc diag

service b2g /system/b2g/b2g
    onrestart restart media

service rilproxy /system/bin/rilproxy
    socket rilproxy stream 660 root system
    socket rilproxyd stream 660 root system
    user root
    group radio cache inet misc audio sdcard_rw net_admin net_raw log

service media /system/bin/mediaserver
    user media
    group system audio camera graphics inet net_bt net_bt_admin net_raw qcom_oncrpc
    ioprio rt 4

service bootanim /system/bin/bootanimation
    user graphics
    group graphics
    disabled
    oneshot

service dbus /system/bin/dbus-daemon --system --nofork
    socket dbus stream 660 bluetooth bluetooth
    user bluetooth
    group bluetooth net_bt_admin

service bluetoothd /system/bin/bluetoothd -n
    socket bluetooth stream 660 bluetooth bluetooth
    socket dbus_bluetooth stream 660 bluetooth bluetooth
    # init.rc does not yet support applying capabilities, so run as root and
    # let bluetoothd drop uid to bluetooth with the right linux capabilities
    group bluetooth net_bt_admin misc
    disabled

service hfag /system/bin/sdptool add --channel=10 HFAG
    user bluetooth
    group bluetooth net_bt_admin
    disabled
    oneshot

service hsag /system/bin/sdptool add --channel=11 HSAG
    user bluetooth
    group bluetooth net_bt_admin
    disabled
    oneshot

service opush /system/bin/sdptool add --channel=12 OPUSH
    user bluetooth
    group bluetooth net_bt_admin
    disabled
    oneshot

service pbap /system/bin/sdptool add --channel=19 PBAP
    user bluetooth
    group bluetooth net_bt_admin
    disabled
    oneshot

service installd /system/bin/installd
    socket installd stream 600 system system

service flash_recovery /system/etc/install-recovery.sh
    oneshot

service racoon /system/bin/racoon
    socket racoon stream 600 system system
    # racoon will setuid to vpn after getting necessary resources.
    group net_admin
    disabled
    oneshot

service mtpd /system/bin/mtpd
    socket mtpd stream 600 system system
    user vpn
    group vpn net_admin net_raw
    disabled
    oneshot

service keystore /system/bin/keystore /data/misc/keystore
    user keystore
    group keystore
    socket keystore stream 666

service dumpstate /system/bin/dumpstate -s
    socket dumpstate stream 0660 shell log
    disabled
    oneshot

 

了解以下内容前,看下/system/core/init/readme.txt对init.rc的解释 蛮有必要的!!!~~

注意:

(1) 一个section的内容从这个标识section的关键字(即symbol为on或者service)开始,到下一个标识section的地方结束。

(2) boot阶段执行的动作都是由boot这个section定义的。(同理init等阶段)

 

知识点4:解析 service (即服务)

1. 首先了解下三个结构体:service、action、command。

init中使用service结构体来保存于service section相关的信息。

三个结构体代码如下:

struct command
{
        /* list of commands in an action */
    struct listnode clist;
 
    int (*func)(int nargs, char **args);
    int nargs;
    char *args[1];
};
//-----------------------------------------------------------    
struct action {
        /* node in list of all actions */
    struct listnode alist;
        /* node in the queue of pending actions */
    struct listnode qlist;
        /* node in list of actions for a trigger */
    struct listnode tlist;
 
    unsigned hash;
    const char *name;
     
    struct listnode commands;
    struct command *current;
};
//------------------------------------------------------------
struct service {
        /* list of all services */
    struct listnode slist;
 
    const char *name;   //service的名字,比如"zygote"
    const char *classname;   //service所属class的名字,默认是"default"
 
    unsigned flags;   //service的属性
    pid_t pid;    //进程号
    time_t time_started;    /* time of last start */
    time_t time_crashed;    /* first crash within inspection window */
    int nr_crashed;         /* number of times crashed within window */
     
    uid_t uid;
    gid_t gid;
    gid_t supp_gids[NR_SVC_SUPP_GIDS];
    size_t nr_supp_gids;
 
    char *seclabel;
 
    struct socketinfo *sockets;
    struct svcenvinfo *envvars;
   
     // 用来存储command信息
    struct action onrestart;  /* Actions to execute on restart. */
     
    /* keycodes for triggering this service via /dev/keychord */
    int *keycodes;
    int nkeycodes;
    int keychord_id;
 
    int ioprio_class;
    int ioprio_pri;
 
    int nargs;  //参数个数
    /* "MUST BE AT THE END OF THE STRUCT" */
    char *args[1];  //用于存储参数
}; /*     ^-------'args' MUST be at the end of this struct! */

 

2.Android的三个双向链表

现在继续知识点3的第2个小点来分析 parse_config方法,回到 parse_new_section方法中解析函数在解析每一个section的时候:

(1) 如果是on,就表明是一个action,则把这个action加入action_list (c)中,比如init、boot都是action。而这个action下面会跟着很多的Commands,解析函数会把这个action下面的所有command链成一个双向链表,保存在action结构体的commands成员(listnode类型)中。

(2) 如果是service,表明是一个服务,则把这个service加入service_list (c)中。而这个service下面有很多options,解析函数会根据这些不同的option执行不同的动作,对服务的信息进行设置,其中包括设置环境信息,创建套接字,设置重启标记等等。

(c)Android维护了三个双向链表,分别是action_list、service_list、action_queue。

action_list存放的是所有要执行的action,
service_list存放的是所有要启动的service,
action_queue存放的是即将执行的action。

 

三个链表的创建是在init_parser.c中定义的:

static list_declare(service_list);
static list_declare(action_list);
static list_declare(action_queue);

而list_declare是在list.h中定义的一个宏:

#define list_declare(name) \
   struct listnode name = { \
        .next = &name, \
        .prev = &name, \
   }

*.rc 脚本中所有 service关键字定义的服务将会添加到 service_list 列表中。
*.rc 脚本中所有 on 关键字开头的项将会被会添加到 action_list 列表中。
每个action列表项都有一个列表,此列表用来保存该段落下的 Commands。

 

3.分析parse_new_section方法。

在这里,通过对zygote的解析进行分析:

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
 

当解析到这段代码时,开始执行parse_serviceparse_line_service两个方法。

parse_service方法:对 service 关键字开头的内容进行解析。操作包括判断service_list 链表中是否已有该service;设置类名(classname)为"default";把service加到全局链表service_list中。

源码如下:

parse_service
 1  static void *parse_service(struct parse_state *state, int nargs, char **args)
 2  {
 3      struct service *svc;
 4      if (nargs < 3) {
 5          parse_error(state, "services must have a name and a program\n");
 6          return 0;
 7      }
 8      if (!valid_name(args[1])) {
 9          parse_error(state, "invalid service name '%s'\n", args[1]);
10          return 0;
11      }
12  
13      svc = service_find_by_name(args[1]);   //查找service_list中是否已有该服务
14      if (svc) {       //若有,则直接返回
15          parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
16          return 0;
17      }
18      
19      nargs -= 2;
20      svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
21      if (!svc) {
22          parse_error(state, "out of memory\n");
23          return 0;
24      }
25      svc->name = args[1];
26      svc->classname = "default";    //设置类名为default
27      memcpy(svc->args, args + 2, sizeof(char*) * nargs);
28      svc->args[nargs] = 0;
29      svc->nargs = nargs;
30      svc->onrestart.name = "onrestart";
31      list_init(&svc->onrestart.commands);
32      list_add_tail(&service_list, &svc->slist);     //添加到service_list中
33      return svc;
34  }

 

在调用此方法后,所有服务的svc->classname都为default,这样将导致service_list中的所有服务都将被执行。

在这里agrs[1]就是zygote,系统会先查找是否已经存在该服务,然后构建一个service svc,进行相关的填充,包括服务名,服务所属的类别名字,以及服务启动带入的参数个数(要减去service和服务名zygote),最后将这个svc加入到service_list全局链表中。随后所做的是对Service的下面几行Option进行解析,比如class,socket,onrestart等等。使用的是parse_line_service函数。

该方法中支持的option如下:

capability   # 暂时未实现
class <name> # 设置名称为name的类别,感觉有点像开机启动service的优先级,默认的class名称为default
console      # 需要在android屏幕上打开控制台
disabled     # 设置后,不能自动的通过class名称启动,必须显式的通过service名称启动
ioprio <rt|be|idle> <0-7> # 设置io优先级
group <groupname> [ <groupname> ]* # 设置服务进程的effective group ID(第一个参数)和supplementary group IDs(第二个到最后)
keycodes <keycodes> [ <keycodes> ]* # keycodes相关
oneshot   # 服务退出时,不再启动,但可以通过名称启动
onrestart # 服务重启时,执行的命令,可能是服务的重启的时候,需要作一些额外的工作
critical  # 是device-critical service,在4分钟内退出超过4次,那么设备会重启到recover模式下
setenv <name> <value> # 设置服务的环境变量
socket <name> <type> <perm> [ <user> [ <group> ]  ] # 为服务创建socket,可以创建多个
user <effectuserid>   # 设置服务进程的effective user ID

来看下parse_line_service函数的源码:

parse_line_service
  1 static void parse_line_service(struct parse_state *state, int nargs, char **args)
  2 {
  3     struct service *svc = state->context;
  4     struct command *cmd;
  5     int kw, kw_nargs;
  6 
  7     if (nargs == 0) {
  8         return;
  9     }
 10     
 11     kw = lookup_keyword(args[0]);
 12     switch (kw) {
 13     case K_capability:
 14         break;
 15     case K_class:
 16         if (nargs != 2) {
 17             parse_error(state, "class option requires a classname\n");
 18         } else {
 19             svc->classname = args[1];
 20         }
 21         break;
 22     case K_console:
 23         svc->flags |= SVC_CONSOLE;
 24         break;
 25     case K_disabled:
 26         svc->flags |= SVC_DISABLED;
 27         break;
 28     case K_group:
 29         if (nargs < 2) {
 30             parse_error(state, "group option requires a group id\n");
 31         } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
 32             parse_error(state, "group option accepts at most %d supp. groups\n",
 33                         NR_SVC_SUPP_GIDS);
 34         } else {
 35             int n;
 36             svc->gid = decode_uid(args[1]);
 37             for (n = 2; n < nargs; n++) {
 38                 svc->supp_gids[n-2] = decode_uid(args[n]);
 39             }
 40             svc->nr_supp_gids = n - 2;
 41         }
 42         break;
 43     case K_oneshot:
 44         svc->flags |= SVC_ONESHOT;
 45         break;
 46     case K_onrestart:
 47         nargs--;
 48         args++;
 49         kw = lookup_keyword(args[0]);
 50         if (!kw_is(kw, COMMAND)) {
 51             parse_error(state, "invalid command '%s'\n", args[0]);
 52             break;
 53         }
 54         kw_nargs = kw_nargs(kw);
 55         if (nargs < kw_nargs) {
 56             parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
 57                 kw_nargs > 2 ? "arguments" : "argument");
 58             break;
 59         }
 60 
 61         cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
 62         cmd->func = kw_func(kw);
 63         cmd->nargs = nargs;
 64         memcpy(cmd->args, args, sizeof(char*) * nargs);
 65         list_add_tail(&svc->onrestart.commands, &cmd->clist);
 66         break;
 67     case K_critical:
 68         svc->flags |= SVC_CRITICAL;
 69         break;
 70     case K_setenv: { /* name value */
 71         struct svcenvinfo *ei;
 72         if (nargs < 2) {
 73             parse_error(state, "setenv option requires name and value arguments\n");
 74             break;
 75         }
 76         ei = calloc(1, sizeof(*ei));
 77         if (!ei) {
 78             parse_error(state, "out of memory\n");
 79             break;
 80         }
 81         ei->name = args[1];
 82         ei->value = args[2];
 83         ei->next = svc->envvars;
 84         svc->envvars = ei;
 85         break;
 86     }
 87     case K_socket: {/* name type perm [ uid gid ] */
 88         struct socketinfo *si;
 89         if (nargs < 4) {
 90             parse_error(state, "socket option requires name, type, perm arguments\n");
 91             break;
 92         }
 93         if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) {
 94             parse_error(state, "socket type must be 'dgram' or 'stream'\n");
 95             break;
 96         }
 97         si = calloc(1, sizeof(*si));
 98         if (!si) {
 99             parse_error(state, "out of memory\n");
100             break;
101         }
102         si->name = args[1];
103         si->type = args[2];
104         si->perm = strtoul(args[3], 0, 8);
105         if (nargs > 4)
106             si->uid = decode_uid(args[4]);
107         if (nargs > 5)
108             si->gid = decode_uid(args[5]);
109         si->next = svc->sockets;
110         svc->sockets = si;
111         break;
112     }
113     case K_user:
114         if (nargs != 2) {
115             parse_error(state, "user option requires a user id\n");
116         } else {
117             svc->uid = decode_uid(args[1]);
118         }
119         break;
120     default:
121         parse_error(state, "invalid option '%s'\n", args[0]);
122     }
123 }

 

这里以class这个keyword为例,会将当前class所属的svc进行类名的变革,变为main类别,类似的socket和onrestart类似。

到此为止整个service都解析完成 ,开始下一个section的内容。

ps:当zygote解析完后,得到
(1) service_list链表将解析后的service全部链接到一起。
(2) socketinfo也是一个双向链表,zygote只有一个socket,会被链接到socketinfo中。
(3) onrestart通过commands 指向一个commands 链表,zygote有三个commands。

但是zygote这个服务进程的启动还没有开始,将在下面分析。

 

知识点5:init启动service

1.启动

1.1 分析queue_builtin_action和action_for_each_trigger

queue_builtin_action(console_init_action, "console_init"); //第二个开机画面显示函数。该函数实现将console_init 这个action添加到action_queue全局链表中。

action_for_each_trigger("boot", action_add_queue_tail); //把boot这个action添加到action_queue链表中

void action_for_each_trigger(const char *trigger,
                             void (*func)(struct action *act))
{
    struct listnode *node;
    struct action *act;
    list_for_each(node, &action_list) {
        act = node_to_item(node, struct action, alist);
        if (!strcmp(act->name, trigger)) {
            func(act);
        }
    }
}

在该函数中,首先遍历action_list链表,找到action,看是否有名字叫boot的trigger存在,我们知道刚才在解析init.rc中的on boot时,将boot这个作为action的name加入到了action_list中去,所以可以找到这个boot的action。成功匹配后调用action_add_queue_tail,将这个action再次加入到action_queue中,等待着执行。

 

1.2 for(;;)循环中执行execute_one_command

void execute_one_command(void)
{
    int ret;

    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
        cur_action = action_remove_queue_head();
        cur_command = NULL;
        if (!cur_action)
            return;
        INFO("processing action %p (%s)\n", cur_action, cur_action->name);
        cur_command = get_first_command(cur_action);
    } else {
        cur_command = get_next_command(cur_action, cur_command);
    }

    if (!cur_command)
        return;

    ret = cur_command->func(cur_command->nargs, cur_command->args);//执行class_start等
    INFO("command '%s' r=%d\n", cur_command->args[0], ret);
}

使用action_remove_queue_head获取action_queue链表中的action后,移除该节点,使用get_first_command获得在action中的命令,比如这里出现的boot和console_init这两个action。针对console_init启动console_init_action这个函数。如果是boot则会对boot这个action所具有的commands链表进行cmd的获取,class_start的func指针函数为do_class_start:

int do_class_start(int nargs, char **args)
{
    //init.rc只有一个参数,就是default
        /* Starting a class does not start services
         * which are explicitly disabled.  They must
         * be started individually.
         */
    //这个方法将从service_list中找到classname为"default"的service,然后调用service_start_if_not_disabled方法。
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}

可以看到提取了命令行的第二个参数入main,core等。在service_for_each_class中遍历service_list查找属于该类的service,如我们前面提到的zygote,查找到后执行service_start_if_not_disabled ----> service_start,至此我们进入了启动service的代码。

service_start
void service_start(struct service *svc, const char *dynamic_args)
{
    struct stat s;
    pid_t pid;
    int needs_console;
    int n;

        /* starting a service removes it from the disabled
         * state and immediately takes it out of the restarting
         * state if it was in there
         */
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING));
    svc->time_started = 0;

        /* running processes require no additional work -- if
         * they're in the process of exiting, we've ensured
         * that they will immediately restart on exit, unless
         * they are ONESHOT
         */
    if (svc->flags & SVC_RUNNING) {
        return;   //如果这个service已经在运行,则不用处理
    }

    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
    if (needs_console && (!have_console)) {
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }
    /*
      service运行在另一个进程中,这个进程也是init的子进程,所以启动service前需要判断对应的可执行文件是否存在,zygote对应的可执行文件是/system/bin/app_process
    */ 
    if (stat(svc->args[0], &s) != 0) {  //通过文件名获取文件信息保存到s的buf中
        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
               svc->args[0]);
        svc->flags |= SVC_DISABLED;
        return;
    }

    NOTICE("starting '%s'\n", svc->name);

    pid = fork();   //调用fork创建子进程

    if (pid == 0) {  //pid=0,代现在运行在子进程中
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        if (properties_inited()) { //得到属性存储空间的信息并加到环境变量中。
            get_property_workspace(&fd, &sz);
            sprintf(tmp, "%d,%d", dup(fd), sz);
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }

        for (ei = svc->envvars; ei; ei = ei->next)
     //添加环境变量信息
            add_environment(ei->name, ei->value);

    //根据socketinfo创建socket
        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type,
                                  si->perm, si->uid, si->gid);
            if (s >= 0) {
        //在环境变量中添加socket信息
                publish_socket(si->name, s);
            }
        }

        if (svc->ioprio_class != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
            }
        }

        if (needs_console) {
            setsid();
            open_console();
        } else {
            zap_stdio();
        }

#if 0
        for (n = 0; svc->args[n]; n++) {
            INFO("args[%d] = '%s'\n", n, svc->args[n]);
        }
        for (n = 0; ENV[n]; n++) {
            INFO("env[%d] = '%s'\n", n, ENV[n]);
        }
#endif

        setpgid(0, getpid());

    /* as requested, set our gid, supplemental gids, and uid */
        if (svc->gid) {
            setgid(svc->gid);
        }
        if (svc->nr_supp_gids) {
            setgroups(svc->nr_supp_gids, svc->supp_gids);
        }
        if (svc->uid) {
            setuid(svc->uid);
        }

        if (!dynamic_args) {
        //执行/system/bin/app_process,这样就进入到app_process的main方法中了。
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
        } else {
            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
            int arg_idx = svc->nargs;
            char *tmp = strdup(dynamic_args);
            char *next = tmp;
            char *bword;

            /* Copy the static arguments */
            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));

            while((bword = strsep(&next, " "))) {
                arg_ptrs[arg_idx++] = bword;
                if (arg_idx == INIT_PARSER_MAXARGS)
                    break;
            }
            arg_ptrs[arg_idx] = '\0';
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
        }
        _exit(127);
    }

    if (pid < 0) {
        ERROR("failed to start '%s'\n", svc->name);
        svc->pid = 0;
        return;
    }

    //父进程init的处理,设置service的信息,如启动时间、进程号,以及状态等。
    svc->time_started = gettime();
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;

    if (properties_inited())
    //每一个service都有一个属性,zygote的属性为init.svc.zygote,现在设置它的值为running。
        notify_service_state(svc->name, "running");
}

 

分析这段代码,主要内容:

a.检查当前service如zygote的flag 即SVC_RUNNING(服务运行中),SVC_DISABLE等。

b.fork一个子进程,子进程中会建立一个socket用于通信,同时使用if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0)执行zygote对应的可执行文件,至此service zygote真正地启动起来。

c.启动service后,会将其属性设置为running。

 

2.补充知识点---重启

1.init中由handle_signal管理进程的重启。一旦出现service崩溃,poll函数会接收到相关文件变化的信息,执行handle_signal中的wait_for_one_process

static int wait_for_one_process(int block)
{
    pid_t pid;
    int status;
    struct service *svc;
    struct socketinfo *si;
    time_t now;
    struct listnode *node;
    struct command *cmd;

    while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );
    if (pid <= 0) return -1;
    INFO("waitpid returned pid %d, status = %08x\n", pid, status);

    svc = service_find_by_pid(pid);
    if (!svc) {
        ERROR("untracked pid %d exited\n", pid);
        return 0;
    }
.....
 svc->flags |= SVC_RESTARTING;

    /* Execute all onrestart commands for this service. */
    list_for_each(node, &svc->onrestart.commands) {
        cmd = node_to_item(node, struct command, clist);
        cmd->func(cmd->nargs, cmd->args);
    }
    notify_service_state(svc->name, "restarting");
    return 0;
}

该函数使用waitpid,找到子进程退出的进程号pid,然后查找到该service,对service中的onrestart这个commands进行操作,如restart media等。同时将service的flag设置为SVC_RESTARTING这样就能够通过restart_processes重新启动该服务进程(见下面第2点)。

2. service进程的重启在restart_processes中进行,它会重启flag为SVC_RESTARTING的服务

 

 

 

 参考:

android系统启动流程启动画面学习之init和init.rc分析