Android启动过程分析——init.c(二)

Part 4

// ==================================================   
// Part 4

union selinux_callback cb;
cb.func_log = klog_write;
selinux_set_callback(SELINUX_CB_LOG, cb);

cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);

selinux_initialize();

/* These directories were necessarily created before initial policy load
 * and therefore need their security context restored to the proper value.
 * This must happen before /dev is populated by ueventd.
 */
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
// ==================================================

这部分与selinux有关,会进行一些设置,并将值写入相关设备节点。

Part 5

// ==================================================
// Part 5
is_charger = !strcmp(bootmode, "charger");
// ==================================================

这部分只有一句话,就是检查bootmode,根据是不是charger模式给is_charger赋值。bootmode是在export_kernel_boot_props()函数里从属性连读取的。我猜测,这个变量的作用是判断是不是充电开机的。因为Android在关机的时候插入充电器,屏幕会点亮,并显示当前点亮,这种开机与正常开机相比,所需要支持的功能要简单得多,所以,init在后面的进程中,会判断是否是充电开机,如果是充电开机,某些过程会直接省略掉。

    // ==================================================
    // Part 6
    INFO("property init\n");
    if (!is_charger)
        property_load_boot_defaults();

    INFO("reading config file\n");
    init_parse_config_file("/init.rc");

    action_for_each_trigger("early-init", action_add_queue_tail);

    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_init");

    /* execute all the boot actions to get us started */
    action_for_each_trigger("init", action_add_queue_tail);

    /* skip mounting filesystems in charger mode */
    if (!is_charger) {
        action_for_each_trigger("early-fs", action_add_queue_tail);
        action_for_each_trigger("fs", action_add_queue_tail);
        action_for_each_trigger("post-fs", action_add_queue_tail);
        action_for_each_trigger("post-fs-data", action_add_queue_tail);
    }

    /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     * wasn't ready immediately after wait_for_coldboot_done
     */
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

    queue_builtin_action(property_service_init_action, "property_service_init");
    queue_builtin_action(signal_init_action, "signal_init");
    queue_builtin_action(check_startup_action, "check_startup");

    if (is_charger) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("early-boot", action_add_queue_tail);
        action_for_each_trigger("boot", action_add_queue_tail);
    }

        /* run all property triggers based on current state of the properties */
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");


#if BOOTCHART
    queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
    // ==================================================

这一部分是init进程的重头戏,我们将重点分析。

INFO("property init\n");
if (!is_charger)
    property_load_boot_defaults();

void property_load_boot_defaults(void)
{
    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}

static void load_properties_from_file(const char *fn)
{
    char *data;
    unsigned sz;

    data = read_file(fn, &sz);

    if(data != 0) {
        load_properties(data);
        free(data);
    }
}

static void load_properties(char *data)
{
    char *key, *value, *eol, *sol, *tmp;

    sol = data;
    while((eol = strchr(sol, '\n'))) {
        key = sol;
        *eol++ = 0;
        sol = eol;

        value = strchr(key, '=');
        if(value == 0) continue;
        *value++ = 0;

        while(isspace(*key)) key++;
        if(*key == '#') continue;
        tmp = value - 2;
        while((tmp > key) && isspace(*tmp)) *tmp-- = 0;

        while(isspace(*value)) value++;
        tmp = eol - 2;
        while((tmp > value) && isspace(*tmp)) *tmp-- = 0;

        property_set(key, value);
    }
}

首先是从PROP_PATH_RAMDISK_DEFAULT指定的设备节点中读取数据,然后解析这些数据,再调用property_set()函数将数据写入property系统。

接下来解析init.rc文件。

INFO("reading config file\n");
init_parse_config_file("/init.rc");

int init_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)
{
    struct parse_state state;
    struct listnode import_list;
    struct listnode *node;
    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;

    list_init(&import_list);
    state.priv = &import_list;

    for (;;) {
        switch (next_token(&state)) {
        case T_EOF:
            state.parse_line(&state, 0, 0);
            goto parser_done;
        case T_NEWLINE:
            state.line++;
            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;
            }
            break;
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }

parser_done:
    list_for_each(node, &import_list) {
         struct import *import = node_to_item(node, struct import, list);
         int ret;

         INFO("importing '%s'", import->filename);
         ret = init_parse_config_file(import->filename);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}

首先读取/init.rc这个文件,然后在parse_config()函数里面解析读到的数据。解析的过程在for循环里。

注意parse_config()里面的parser_done这个标签。list_for_each是一个宏,作用是遍历解析到的import节点。然后递归的调用init_parse_config_file()这个函数来遍历import进来的.rc文件。

之后就是一堆action_for_each_trigger()和queue_builtin_action()。

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_for_each_trigge的作用是遍历action_list,将act的名称与指定的trigger比较,如果一致,就将act传递给func函数并执行。

在这里,就是在init.rc解析出来的action_list中查找指定的trigger,然后将这些trigger对应的act通过函数action_add_queue_tail添加到队列中:

void action_add_queue_tail(struct action *act)
{
    if (list_empty(&act->qlist)) {
        list_add_tail(&action_queue, &act->qlist);
    }
}

void list_add_tail(struct listnode *head, struct listnode *item)
{
    item->next = head;
    item->prev = head->prev;
    head->prev->next = item;
    head->prev = item;
}
void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
{
    struct action *act;
    struct command *cmd;

    act = calloc(1, sizeof(*act));
    act->name = name;
    list_init(&act->commands);
    list_init(&act->qlist);

    cmd = calloc(1, sizeof(*cmd));
    cmd->func = func;
    cmd->args[0] = name;
    list_add_tail(&act->commands, &cmd->clist);

    list_add_tail(&action_list, &act->alist);
    action_add_queue_tail(act);
}

可以看到,这个函数的最后也是调用action_add_queue_tail函数。所以,这两个函数的作用是一样的,区别在于一个是从init.rc文件得到act,一个是从函数得到的。

在《Android框架揭秘》中,没有看到queue_builtin_action()的介绍,所以,我猜测,这是Android4.4与Android4.2有区别的地方。

至于为什么要这样做,我猜测,Google是将可以配置的act放在了init.rc中,而不可配置,必须要有的act就放在了代码中写死。

下面来分析一下queue_builtin_action中的各种act来源。

static const char *coldboot_done = "/dev/.coldboot_done";

static int wait_for_coldboot_done_action(int nargs, char **args)
{
    int ret;
    INFO("wait for %s\n", coldboot_done);
    ret = wait_for_file(coldboot_done, COMMAND_RETRY_TIMEOUT);
    if (ret)
        ERROR("Timed out waiting for %s\n", coldboot_done);
    return ret;
}

当系统冷启动完成之后,会创建/dev/.coldboot_done文件。这里就是等待冷启动结束,再执行下面的动作。

/*
 * Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
 * by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
 * Does nothing if Hardware RNG is not present.
 *
 * Since we don't yet trust the quality of Hardware RNG, these bytes are not
 * mixed into the primary pool of Linux RNG and the entropy estimate is left
 * unmodified.
 *
 * If the HW RNG device /dev/hw_random is present, we require that at least
 * 512 bytes read from it are written into Linux RNG. QA is expected to catch
 * devices/configurations where these I/O operations are blocking for a long
 * time. We do not reboot or halt on failures, as this is a best-effort
 * attempt.
 */
static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
{
    int result = -1;
    int hwrandom_fd = -1;
    int urandom_fd = -1;
    char buf[512];
    ssize_t chunk_size;
    size_t total_bytes_written = 0;

    hwrandom_fd = TEMP_FAILURE_RETRY(
            open("/dev/hw_random", O_RDONLY | O_NOFOLLOW));
    if (hwrandom_fd == -1) {
        if (errno == ENOENT) {
          ERROR("/dev/hw_random not found\n");
          /* It's not an error to not have a Hardware RNG. */
          result = 0;
        } else {
          ERROR("Failed to open /dev/hw_random: %s\n", strerror(errno));
        }
        goto ret;
    }

    urandom_fd = TEMP_FAILURE_RETRY(
            open("/dev/urandom", O_WRONLY | O_NOFOLLOW));
    if (urandom_fd == -1) {
        ERROR("Failed to open /dev/urandom: %s\n", strerror(errno));
        goto ret;
    }

    while (total_bytes_written < sizeof(buf)) {
        chunk_size = TEMP_FAILURE_RETRY(
                read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
        if (chunk_size == -1) {
            ERROR("Failed to read from /dev/hw_random: %s\n", strerror(errno));
            goto ret;
        } else if (chunk_size == 0) {
            ERROR("Failed to read from /dev/hw_random: EOF\n");
            goto ret;
        }

        chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
        if (chunk_size == -1) {
            ERROR("Failed to write to /dev/urandom: %s\n", strerror(errno));
            goto ret;
        }
        total_bytes_written += chunk_size;
    }

    INFO("Mixed %d bytes from /dev/hw_random into /dev/urandom",
                total_bytes_written);
    result = 0;

ret:
    if (hwrandom_fd != -1) {
        close(hwrandom_fd);
    }
    if (urandom_fd != -1) {
        close(urandom_fd);
    }
    memset(buf, 0, sizeof(buf));
    return result;
}

这个函数比较复杂,好像是和随机数的生成机制有关。从硬件随机数读512个字节写入软件随机数。

static int keychord_init_action(int nargs, char **args)
{
    keychord_init();
    return 0;
}

void keychord_init()
{
    int fd, ret;

    service_for_each(add_service_keycodes);

    /* nothing to do if no services require keychords */
    if (!keychords)
        return;

    fd = open("/dev/keychord", O_RDWR);
    if (fd < 0) {
        ERROR("could not open /dev/keychord\n");
        return;
    }
    fcntl(fd, F_SETFD, FD_CLOEXEC);

    ret = write(fd, keychords, keychords_length);
    if (ret != keychords_length) {
        ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno);
        close(fd);
        fd = -1;
    }

    free(keychords);
    keychords = 0;

    keychord_fd = fd;
}

这个就是打开多点触控的设备节点。

static char console_name[PROP_VALUE_MAX] = "/dev/console";

static int console_init_action(int nargs, char **args)
{
    int fd;

    if (console[0]) {
        snprintf(console_name, sizeof(console_name), "/dev/%s", console);
    }

    fd = open(console_name, O_RDWR);
    if (fd >= 0)
        have_console = 1;
    close(fd);

    if( load_565rle_image(INIT_IMAGE_FILE) ) {
        fd = open("/dev/tty0", O_WRONLY);
        if (fd >= 0) {
            const char *msg;
                msg = "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"  // console is 40 cols x 30 lines
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "             A N D R O I D ";
            write(fd, msg, strlen(msg));
            close(fd);
        }
    }
    return 0;
}

这里是打开一个设备节点,然后显示INIT_IMAGE_FILE指定的开机画面(这里的开机画面是565rle格式的)。

static int property_service_init_action(int nargs, char **args)
{
    /* read any property files on system or data and
     * fire up the property service.  This must happen
     * after the ro.foo properties are set above so
     * that /data/local.prop cannot interfere with them.
     */
    start_property_service();
    return 0;
}

void start_property_service(void)
{
    int fd;

    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
    load_override_properties();
    /* Read persistent properties after all default values have been loaded. */
    load_persistent_properties();

    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
    if(fd < 0) return;
    fcntl(fd, F_SETFD, FD_CLOEXEC);
    fcntl(fd, F_SETFL, O_NONBLOCK);

    listen(fd, 8);
    property_set_fd = fd;
}

这里就是启动property系统。一次载入PROP_PATH_SYSTEM_BUILD、PROP_PATH_SYSTEM_BUILD指定的文件中的属性,覆盖的属性、persist属性。

属性值的更改仅能在init进程中进行,即一个进程若想修改属性值,必须向init进程提交申请,为此init进程生成“/dev/socket/property_service”套接字,以接受其他进程提交的申请。

static int signal_init_action(int nargs, char **args)
{
    signal_init();
    return 0;
}

void signal_init(void)
{
    int s[2];

    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = sigchld_handler;
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);

    /* create a signalling mechanism for the sigchld handler */
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
        signal_fd = s[0];
        signal_recv_fd = s[1];
        fcntl(s[0], F_SETFD, FD_CLOEXEC);
        fcntl(s[0], F_SETFL, O_NONBLOCK);
        fcntl(s[1], F_SETFD, FD_CLOEXEC);
        fcntl(s[1], F_SETFL, O_NONBLOCK);
    }

    handle_signal();
}

void handle_signal(void)
{
    char tmp[32];

    /* we got a SIGCHLD - reap and restart as needed */
    read(signal_recv_fd, tmp, sizeof(tmp));
    while (!wait_for_one_process(0))
        ;
}

首先,将sigchld_handler()函数注册为SIGCHLD信号处理函数。然后,创建socket,来接收信号。然后,开始处理SIGCHLD信号。

static int check_startup_action(int nargs, char **args)
{
    /* make sure we actually have all the pieces we need */
    if ((get_property_set_fd() < 0) ||
        (get_signal_fd() < 0)) {
        ERROR("init startup failure\n");
        exit(1);
    }

        /* signal that we hit this point */
    unlink("/dev/.booting");

    return 0;
}

这里仅仅是做个检查。

static int queue_property_triggers_action(int nargs, char **args)
{
    queue_all_property_triggers();
    /* enable property triggers */
    property_triggers_enabled = 1;
    return 0;
}

void queue_all_property_triggers()
{
    struct listnode *node;
    struct action *act;
    list_for_each(node, &action_list) {
        act = node_to_item(node, struct action, alist);
        if (!strncmp(act->name, "property:", strlen("property:"))) {
            /* parse property name and value
               syntax is property:<name>=<value> */
            const char* name = act->name + strlen("property:");
            const char* equals = strchr(name, '=');
            if (equals) {
                char prop_name[PROP_NAME_MAX + 1];
                char value[PROP_VALUE_MAX];
                int length = equals - name;
                if (length > PROP_NAME_MAX) {
                    ERROR("property name too long in trigger %s", act->name);
                } else {
                    memcpy(prop_name, name, length);
                    prop_name[length] = 0;

                    /* does the property exist, and match the trigger value? */
                    property_get(prop_name, value);
                    if (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*")) {
                        action_add_queue_tail(act);
                    }
                }
            }
        }
    }
}

在init.rc中定义了很多on propertychange的act,这里就是根据当前属性的状态,来触发这些act。

#if BOOTCHART
static int bootchart_init_action(int nargs, char **args)
{
    bootchart_count = bootchart_init();
    if (bootchart_count < 0) {
        ERROR("bootcharting init failure\n");
    } else if (bootchart_count > 0) {
        NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS);
    } else {
        NOTICE("bootcharting ignored\n");
    }

    return 0;
}
#endif

bootchart是一个用于分析开机启动的实用程序,可以借助于它来分析开机过程中cpu的使用情况和各个进程的启动耗时。

Part 7

这一部分是init进程的事件处理循环。

// ==================================================
// Part 7
for(;;) {
    int nr, i, timeout = -1;

    // 在确认时间发生前,先要在action_list的命令中确认是否有尚未执行的命令,并执行之。
    execute_one_command();

    // 当紫禁城终止退出时,此命令用于重启或终止子进程。
    restart_processes();

    // 注册init进程监视的文件描述符。
    if (!property_set_fd_init && get_property_set_fd() > 0) {
        ufds[fd_count].fd = get_property_set_fd();
        ufds[fd_count].events = POLLIN;
        ufds[fd_count].revents = 0;
        fd_count++;
        property_set_fd_init = 1;
    }
    if (!signal_fd_init && get_signal_fd() > 0) {
        ufds[fd_count].fd = get_signal_fd();
        ufds[fd_count].events = POLLIN;
        ufds[fd_count].revents = 0;
        fd_count++;
        signal_fd_init = 1;
    }
    if (!keychord_fd_init && get_keychord_fd() > 0) {
        ufds[fd_count].fd = get_keychord_fd();
        ufds[fd_count].events = POLLIN;
        ufds[fd_count].revents = 0;
        fd_count++;
        keychord_fd_init = 1;
    }

    if (process_needs_restart) {
        timeout = (process_needs_restart - gettime()) * 1000;
        if (timeout < 0)
            timeout = 0;
    }

    if (!action_queue_empty() || cur_action)
        timeout = 0;

#if BOOTCHART
    if (bootchart_count > 0) {
        if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
            timeout = BOOTCHART_POLLING_MS;
        if (bootchart_step() < 0 || --bootchart_count == 0) {
            bootchart_finish();
            bootchart_count = 0;
        }
    }
#endif

    nr = poll(ufds, fd_count, timeout);
    if (nr <= 0)
        continue;

    for (i = 0; i < fd_count; i++) {
        if (ufds[i].revents == POLLIN) {
            if (ufds[i].fd == get_property_set_fd()) // 处理属性变更请求
                handle_property_set_fd();
            else if (ufds[i].fd == get_keychord_fd()) // 处理多点触控设备静秋
                handle_keychord();
            else if (ufds[i].fd == get_signal_fd()) // 处理SIGCHLD信号
                handle_signal();
        }
    }
}
// ==================================================
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值