《深入理解Android:卷1》-init

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011391629/article/details/80071149

友情提示:师傅说先了解概念,贴的代码都没做分析,只是学习笔记,想看源码解析书上有详细解释。如果以后工作需要,我也会深入学习的。

init是Linux系统中用户空间的第一个进程,而Android是基于Linux内核的,所以init也是Android系统中用户空间的第一个进程,它的进程号是1。

init有两个最重要的职责。

  • init进程负责创建系统中的几个关键进程。
  • Android系统有很多属性,于是init提供了一个property service(属性服务)来管理它们。

system/core/init/init.c,init进程的入口函数是main

int main(int argc, char **argv)
{
    int device_fd = -1;
    int property_set_fd = -1;
    int signal_recv_fd = -1;
    int keychord_fd = -1;
    int fd_count;
    int s[2];
    int fd;
    struct sigaction act;
    char tmp[PROP_VALUE_MAX];
    struct pollfd ufds[4];
    char *tmpdev;
    char* debuggable;

    //设置子进程退出的信号处理函数
    act.sa_handler = sigchld_handler;
    act.sa_flags = SA_NOCLDSTOP;
    act.sa_mask = 0;
    act.sa_restorer = NULL;
    sigaction(SIGCHLD, &act, 0);

    /* clear the umask */
    umask(0);

    //创建文件夹,挂载设备
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);

    mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
    mkdir("/dev/pts", 0755);
    mkdir("/dev/socket", 0755);
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);

        /* We must have some place other than / to create the
         * device nodes for kmsg and null, otherwise we won't
         * be able to remount / read-only later on.
         * Now that tmpfs is mounted on /dev, we can actually
         * talk to the outside world.
         */
    //重定向标准输入/输出/dev/_null_
    open_devnull_stdio();
    log_init();

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

    /* pull the kernel commandline and ramdisk properties file in */
    qemu_init();
    import_kernel_cmdline(0);

    get_hardware_name();
    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
    parse_config_file(tmp);

    action_for_each_trigger("early-init", action_add_queue_tail);
    drain_action_queue();

    INFO("device init\n");
    device_fd = device_init();

    property_init();

    // only listen for keychords if ro.debuggable is true
    keychord_fd = open_keychord();

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

    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);
    }
    }

    if (qemu[0])
        import_kernel_cmdline(1); 

    if (!strcmp(bootmode,"factory"))
        property_set("ro.factorytest", "1");
    else if (!strcmp(bootmode,"factory2"))
        property_set("ro.factorytest", "2");
    else
        property_set("ro.factorytest", "0");

    property_set("ro.serialno", serialno[0] ? serialno : "");
    property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");
    property_set("ro.baseband", baseband[0] ? baseband : "unknown");
    property_set("ro.carrier", carrier[0] ? carrier : "unknown");
    property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");

    property_set("ro.hardware", hardware);
    snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
    property_set("ro.revision", tmp);

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

        /* 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.
         */
    property_set_fd = start_property_service();

    /* 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);
    }

    /* make sure we actually have all the pieces we need */
    if ((device_fd < 0) ||
        (property_set_fd < 0) ||
        (signal_recv_fd < 0)) {
        ERROR("init startup failure\n");
        return 1;
    }

    /* execute all the boot actions to get us started */
    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;     

    ufds[0].fd = device_fd;
    ufds[0].events = POLLIN;
    ufds[1].fd = property_set_fd;
    ufds[1].events = POLLIN;
    ufds[2].fd = signal_recv_fd;
    ufds[2].events = POLLIN;
    fd_count = 3;

    if (keychord_fd > 0) {
        ufds[3].fd = keychord_fd;
        ufds[3].events = POLLIN;
        fd_count++;
    } else {
        ufds[3].events = 0;
        ufds[3].revents = 0;
    }

#if BOOTCHART
    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");
    }
#endif

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

        for (i = 0; i < fd_count; i++)
            ufds[i].revents = 0;

        drain_action_queue();
        restart_processes();

        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                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;

        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);
        if (ufds[3].revents == POLLIN)
            handle_keychord(keychord_fd);
    }

    return 0;
}

init的工作流程可精简为四个部分:

  • 解析两个配置文件(init.rc,与硬件平台相关的配置文件)
  • 执行各个阶段的动作
  • 调用property_init初始化属性相关的资源,并通过property_start_service启动属性服务
  • init进入一个无限循环,并且等待一些事情的发生。

init.rc解析

system/core/init/parser.c

int parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0);  //读取配置文件内容,这个文件时init.rc
    if (!data) return -1;

    parse_config(fn, data); //解析在parse_config函数完成
    DUMP();
    return 0;
}

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

    nargs = 0;
    state.filename = fn;
    state.line = 1;
    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:
            if (nargs) {
                int kw = lookup_keyword(args[0]);  //得到关键字类型
                if (kw_is(kw, SECTION)) {          //判断关键字类型是不是SECTION
                    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 < SVC_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }
}

parse_config首先会配置文件中的section,然后针对不同的section使用不同的解析函数来解析。

属性服务

Android平台的属性服务(property service)类似Windows平台的注册表。系统重启或应用程序重启,还能根据之前设置的属性,进行初始化。与属性服务有关的代码有两行:

  • property_init();
  • property_set_fd = start_property_service();

属性服务初始化

创建存储空间

system/core/init/property_init.c

static int init_property_area(void)
{
    prop_area *pa;

    if(pa_info_array)
        return -1;

    if(init_workspace(&pa_workspace, PA_SIZE))
        return -1;

    fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);

    pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);

    pa = pa_workspace.data;
    memset(pa, 0, PA_SIZE);
    pa->magic = PROP_AREA_MAGIC;
    pa->version = PROP_AREA_VERSION;

    /* plug into the lib property services */
    __system_property_area__ = pa;

    return 0;
}

虽然属性区域是由init进程创建的,但Android系统希望其他进程也能读取这块内存里的东西。这需要两个工作:

  • 把属性区域创建在共享内存上,而共享内存是可以跨进程的。
  • 利用gcc的constructor属性,这个属性指明了一个函数(__libc_prenit),这个函数内部将完成共享内存到本地进程的映射工作

客户端进程获取存储空间

bionic/libc/bionic/system_properties.c

int __system_properties_init(void)
{
    prop_area *pa;
    int s, fd;
    unsigned sz;
    char *env;

    if(__system_property_area__ != ((void*) &dummy_props)) {
        return 0;
    }

    env = getenv("ANDROID_PROPERTY_WORKSPACE");
    if (!env) {
        return -1;
    }
    fd = atoi(env);
    env = strchr(env, ',');
    if (!env) {
        return -1;
    }
    sz = atoi(env + 1);

    pa = mmap(0, sz, PROT_READ, MAP_SHARED, fd, 0);

    if(pa == MAP_FAILED) {
        return -1;
    }

    if((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION)) {
        munmap(pa, sz);
        return -1;
    }

    __system_property_area__ = pa;
    return 0;
}

客户端进程可以直接读取属性空间,但没有权限设置属性。

属性服务器

启动属性服务器

init进程会启动一个属性服务器,而客户端只能通过与属性服务器交互来设置属性。

system/core/init/property_service.c

int start_property_service(void)
{
    int fd;

    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
    load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);
    /* 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 -1;
    fcntl(fd, F_SETFD, FD_CLOEXEC);
    fcntl(fd, F_SETFL, O_NONBLOCK);

    listen(fd, 8);
    return fd;
}

属性服务创建了一个用来接收请求的socket。

处理设置属性请求

验收请求的地方在init进程中,当属性服务器收到客户端请求时,init会调用handle_property_set_fd进行处理。

system/core/init/property_service.c

void handle_property_set_fd(int fd)
{
    prop_msg msg;
    int s;
    int r;
    int res;
    struct ucred cr;
    struct sockaddr_un addr;
    socklen_t addr_size = sizeof(addr);
    socklen_t cr_size = sizeof(cr);

    if ((s = accept(fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
        return;
    }

    /* Check socket options here */
    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
        close(s);
        ERROR("Unable to recieve socket options\n");
        return;
    }

    r = recv(s, &msg, sizeof(msg), 0);
    close(s);
    if(r != sizeof(prop_msg)) {
        ERROR("sys_prop: mis-match msg size recieved: %d expected: %d\n",
          r, sizeof(prop_msg));
        return;
    }

    switch(msg.cmd) {
    case PROP_MSG_SETPROP:
        msg.name[PROP_NAME_MAX-1] = 0;
        msg.value[PROP_VALUE_MAX-1] = 0;

        if(memcmp(msg.name,"ctl.",4) == 0) {
            if (check_control_perms(msg.value, cr.uid, cr.gid)) {
                handle_control_message((char*) msg.name + 4, (char*) msg.value);
            } else {
                ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n",
                        msg.name + 4, msg.value, cr.uid, cr.pid);
            }
        } else {
            if (check_perms(msg.name, cr.uid, cr.gid)) {
                property_set((char*) msg.name, (char*) msg.value);
            } else {
                ERROR("sys_prop: permission denied uid:%d  name:%s\n",
                      cr.uid, msg.name);
            }
        }
        break;

    default:
        break;
    }
}

当客户端的权限满足要求时,init会调用property_set进行相关处理。

system/core/init/property_service.c

int property_set(const char *name, const char *value)
{
    prop_area *pa;
    prop_info *pi;

    int namelen = strlen(name);
    int valuelen = strlen(value);

    if(namelen >= PROP_NAME_MAX) return -1;
    if(valuelen >= PROP_VALUE_MAX) return -1;
    if(namelen < 1) return -1;

    pi = (prop_info*) __system_property_find(name);

    if(pi != 0) {
        /* ro.* properties may NEVER be modified once set */
        if(!strncmp(name, "ro.", 3)) return -1;

        pa = __system_property_area__;
        update_prop_info(pi, value, valuelen);
        pa->serial++;
        __futex_wake(&pa->serial, INT32_MAX);
    } else {
        pa = __system_property_area__;
        if(pa->count == PA_COUNT_MAX) return -1;

        pi = pa_info_array + pa->count;
        pi->serial = (valuelen << 24);
        memcpy(pi->name, name, namelen + 1);
        memcpy(pi->value, value, valuelen + 1);

        pa->toc[pa->count] =
            (namelen << 24) | (((unsigned) pi) - ((unsigned) pa));

        pa->count++;
        pa->serial++;
        __futex_wake(&pa->serial, INT32_MAX);
    }
    /* If name starts with "net." treat as a DNS property. */
    if (strncmp("net.", name, strlen("net.")) == 0)  {
        if (strcmp("net.change", name) == 0) {
            return 0;
        }
       /*
        * The 'net.change' property is a special property used track when any
        * 'net.*' property name is updated. It is _ONLY_ updated here. Its value
        * contains the last updated 'net.*' property.
        */
        property_set("net.change", name);
    } else if (persistent_properties_loaded &&
            strncmp("persist.", name, strlen("persist.")) == 0) {
        /*
         * Don't write properties to disk until after we have read all default properties
         * to prevent them from being overwritten by default values.
     */
        write_persistent_property(name, value);
    }
    property_changed(name, value);
    return 0;
}

客户端发送请求

客户端通过property_set发送请求,property_set由libcutils库提供

system/core/libcutils/properties.c

int property_set(const char *key, const char *value)
{
    prop_msg msg;
    unsigned resp;

    if(key == 0) return -1;
    if(value == 0) value = "";

    if(strlen(key) >= PROP_NAME_MAX) return -1;
    if(strlen(value) >= PROP_VALUE_MAX) return -1;

    msg.cmd = PROP_MSG_SETPROP;
    strcpy((char*) msg.name, key);
    strcpy((char*) msg.value, value);

    return send_prop_msg(&msg);
}

static int send_prop_msg(prop_msg *msg)
{
    int s;
    int r;

    s = socket_local_client(PROP_SERVICE_NAME, 
                        ANDROID_SOCKET_NAMESPACE_RESERVED,
                        SOCK_STREAM);
    if(s < 0) return -1;

    while((r = send(s, msg, sizeof(prop_msg), 0)) < 0) {
        if((errno == EINTR) || (errno == EAGAIN)) continue;
        break;
    }

    if(r == sizeof(prop_msg)) {
        r = 0;
    } else {
        r = -1;
    }

    close(s);
    return r;
}

今日单词

  • parser:解析程序,解析器
  • property:性质,性能

猜你喜欢

转载自blog.csdn.net/u011391629/article/details/80071149