After analyzing the init.rc script in the previous two sections, it’s now time to look at how to start the Zygote Service. The zygote service is not launched in the Boot Action.

I added the necessary logs to the init module to analyze the Zygote Service startup process.

. [3.164551] init: do_class_start args[1]=main Service_for_each_classname =main [3.164583] init: Service_for_each_class service name=netd [3.164596] init: service_STARt_IF_not_disabled name=netd [3.164966] init: Starting service 'netd'... [3.165309] init: service_for_each_class service name=debuggerd [3.165319] Service_start_if_not_disabled Name = DEBUggerd [3.165634] Init: Starting service 'debuggerd'... [3.166017] init: service_for_each_class service name=debuggerd64 [3.166030] Service_start_if_not_disabled name=debuggerd64 [3.166114] init: Cannot find '/system/bin/debuggerd64', disabling 'debuggerd64' [3.166126] init: Service_for_each_class service name=ril-daemon [3.166137] init: Service_start_if_not_disabled name=ril-daemon [3.166505] init: Starting service 'ril-daemon'... [3.166922] init: service_for_each_class service name= DRM Service_start_if_not_disabled Name = DRM [3.167203] init: Starting service 'DRM '... [3.167557] Service_for_each_class service name=media [3.167569] Service_start_if_not_disabled Name =media [3.167799] init: Starting service 'media'... [3.168143] init: service_for_each_class service name=installd [3.168155] Service_start_if_not_disabled Name =installd [3.168400] Init: Starting service 'installd'... [3.168891] Init: Service_for_each_class service name=flash_recovery [3.168919] Service_start_if_not_disabled name=flash_recovery [3.168978] init: Cannot find '/system/bin/install-recovery.sh', disabling 'flash_recovery' [3.168991] init: Service_for_each_class service name=racoon [3.169002] init: service name= MTPD [3.169013] init: Service_for_each_class service name=keystore [3.169023] init: Service_start_if_not_disabled name=keystore [3.169286] init: Starting service 'keystore'... [3.169659] Init: service_for_each_class service name= DumpState [3.169670] init: Service_for_each_class service name= MDNSD [3.169687] init: Service_for_each_class service name=uncrypt [3.169697] init: Service_for_each_class service name=pre-recovery [3.169708] init: Service_for_each_class service name=bridgemgrd [3.169717] init Service_start_if_not_disabled Name =bridgemgrd [3.169950] init: Starting service 'bridgemgrd'... [3.170275] init: service_for_each_class service name=qmuxd Service_start_if_not_disabled Name =qmuxd [3.170495] init: Starting service 'qmuxd'... [3.170809] init: service_for_each_class service name=netmgrd [3.170820] Service_start_if_not_disabled Name =netmgrd [3.173013] Init: Starting service 'netmgrd'... [3.173532] sensors: Service_for_each_class service name=sensors [3.173543] Service_start_if_not_disabled name=sensors [3.173821] init: Starting service 'sensors'... [3.174187] init: service_for_each_class service name=irsc_util Service_start_if_not_disabled name=irsc_util [3.174422] init: Starting service 'irsc_util'... [3.174717] Service name= p2P_supplicant [3.174735] Service name=wpa_supplicant [3.174747] Service_for_each_class service name=bdAddrLoader [3.174757] init: Service_start_if_not_disabled Name =bdAddrLoader [3.175139] init: Starting service 'bdAddrLoader'... [3.175866] init: service_for_each_class service name=bugreport [3.175884] Service_for_each_class service name=mpdecision [3.175896] Service_for_each_class service name=ssr_ramdump [3.175910] init Service_start_if_not_disabled name=ssr_ramdump [3.175924] init: Service_for_each_class service name=thermal-engine [3.175937] Service_start_if_not_disabled Name =thermal-engine [3.176415] Init: Starting service 'thermal-engine'... [3.176925] service name=zygote [3.176939] init: Service_start_if_not_disabled name=zygote [3.177500] init: Starting service 'zygote'... [3.178336] Init: Command 'class_start main' action=nonencrypted (/init.rc:496) Returned 0 took 0.01s......Copy the code

It is not hard to see from the log that the zygote service is finally started by executing the command at line 496 in the init.rc script.

system/core/rootdir/init.rc

. 495 on nonencrypted 496 class_start main 497 class_start late_start ......Copy the code

Now it’s clear that Nonencrypted Action will start all services declared main by classname, including Zygote service.

Consider the entry function main in init. CPP. We examined the init_parse_config_file function in the Android source code parsing init.rc script section. After parsing the RC script, you end up with two linked lists: Action and Service.

system/core/init/init_parser.cpp

static list_declare(service_list);
static list_declare(action_list);
static list_declare(action_queue);
Copy the code

Action_queue is also a double-linked queue, which is used to store actions that are about to be executed. The commands in the actions will be executed one by one. This is done through the execute_one_command function. Restart_processes restarts dead services.

Action_for_each_trigger and queue_builtin_action add actions to the end of the double-linked action_queue. Execute_one_command fetches one Action at a time from its head. Then run the commands it contains.

system/core/init/init.cpp

.int main(int argc, char** argv) {...// Parse the init.rc script
    init_parse_config_file("/init.rc");
    
    action_for_each_trigger("early-init", action_add_queue_tail);
    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); .while (true) {
        if(! waiting_for_exec) { execute_one_command(); restart_processes(); }... }return 0;
}
Copy the code

The command to run now is class_start main, and the pointer to the function that cur_command func points to is equal to do_class_start, which is defined in builtins.cpp.

system/core/init/init.cpp

.void execute_one_command(a) {
    Timer t;

    char cmd_str[256] = "";
    char name_str[256] = "";

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

        build_triggers_string(name_str, sizeof(name_str), cur_action);

        INFO("processing action %p (%s)\n", cur_action, name_str);
        cur_command = get_first_command(cur_action);
    } else {
        cur_command = get_next_command(cur_action, cur_command);
    }

    if(! cur_command) {return;
    }

    int result = cur_command->func(cur_command->nargs, cur_command->args);

    if (klog_get_level() >= KLOG_INFO_LEVEL) {
        for (int i = 0; i < cur_command->nargs; i++) {
            strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));
            if (i < cur_command->nargs - 1) {
                strlcat(cmd_str, "".sizeof(cmd_str)); }}char source[256];
        if (cur_command->filename) {
            snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);
        } else {
            *source = '\ 0';
        }
        NOTICE("Command '%s' action=%s%s returned %d took %.2fs\n",
             cmd_str, cur_action ? name_str : "", source, result, t.duration()); }}Copy the code

The service_for_each_class function is called in the do_class_start function.

system/core/init/builtins.cpp

int do_class_start(int nargs, char **args)
{
        /* Starting a class does not start services * which are explicitly disabled. They must * be started individually. */
	//NOTICE("do_class_start args[1]=%s\n",args[1]);
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}
Copy the code

The service_for_each_class function is defined in init_parser. CPP, which internally iterates through the service_list double-linked list and then converts each item to the corresponding service structure by calling node_to_item. Finally, the service with the same classname is passed to the function specified by the function pointer func.

system/core/init/init_parser.cpp

void service_for_each_class(const char *classname,
                            void (*func)(struct service *svc))
{
	//NOTICE("service_for_each_class service classname=%s\n",classname);
    struct listnode *node;
    struct service *svc;
    list_for_each(node, &service_list) {
        svc = node_to_item(node, struct service, slist);
        if (!strcmp(svc->classname, classname)) {
			//NOTICE("service_for_each_class service name=%s\n",svc->name);func(svc); }}}Copy the code

The service_start_if_not_disabled function is called to start the corresponding service.

system/core/init/builtins.cpp

static void service_start_if_not_disabled(struct service *svc)
{
    if(! (svc->flags & SVC_DISABLED)) {//NOTICE("service_start_if_not_disabled name=%s\n",svc->name);
        service_start(svc, NULL);
    } else{ svc->flags |= SVC_DISABLED_START; }}Copy the code

system/core/init/init.cpp

void service_start(struct service *svc, const char *dynamic_args)
{
    // Clear some flags (disable, restart, reset, restart, disable boot)
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
    svc->time_started = 0;

    // Running processes need no other work - if they are exiting the process, we ensure that they will be restarted immediately upon exiting, unless they are ONESHOT.
    if (svc->flags & SVC_RUNNING) {
        return;
    }

    bool needs_console = (svc->flags & SVC_CONSOLE);
    if(needs_console && ! have_console) { ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    struct stat s;
    if (stat(svc->args[0], &s) ! =0) {
        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;
    }

    char* scon = NULL;
    if (is_selinux_enabled() > 0) {
        if (svc->seclabel) {
            scon = strdup(svc->seclabel);
            if(! scon) { ERROR("Out of memory while starting '%s'\n", svc->name);
                return; }}else {
            char *mycon = NULL, *fcon = NULL;

            INFO("computing context for service '%s'\n", svc->args[0]);
            int rc = getcon(&mycon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
            }

            rc = getfilecon(svc->args[0], &fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                freecon(mycon);
                return;
            }

            rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
            if (rc == 0&&!strcmp(scon, mycon)) {
                ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);
            }
            freecon(mycon);
            freecon(fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
            }
        }
    }

    NOTICE("Starting service '%s'... \n", svc->name);

    pid_t pid = fork();
    // the fork call returns pid equals 0 to indicate child execution
    if (pid == 0) {
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        umask(077);
        if (properties_initialized()) {
            get_property_workspace(&fd, &sz);
            snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }
        // Iterate through the list of environment variables and call add_environment to set the environment variables
        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);
        // Iterate over the socket list and call create_socket to create the 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));ints = create_socket(si->name, socket_type, si->perm, si->uid, si->gid, si->socketcon ? : scon);if (s >= 0) {
                / / release the socket
                publish_socket(si->name, s);
            }
        }

        freecon(scon);
        scon = NULL;
        // Write pid to the specified file
        if (svc->writepid_files_) {
            std: :string pid_str = android::base::StringPrintf("%d", pid);
            for (auto& file : *svc->writepid_files_) {
                if(! android::base::WriteStringToFile(pid_str, file)) { ERROR("couldn't write %s to %s: %s\n", pid_str.c_str(), file.c_str(), strerror(errno)); }}}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 (false) {
            for (size_t n = 0; svc->args[n]; n++) {
                INFO("args[%zu] = '%s'\n", n, svc->args[n]);
            }
            for (size_t n = 0; ENV[n]; n++) {
                INFO("env[%zu] = '%s'\n", n, ENV[n]);
            }
        }

        setpgid(0, getpid());

        // Set the GID, add the GID, and uid as required.
        if (svc->gid) {
            if(setgid(svc->gid) ! =0) {
                ERROR("setgid failed: %s\n", strerror(errno));
                _exit(127); }}if (svc->nr_supp_gids) {
            if(setgroups(svc->nr_supp_gids, svc->supp_gids) ! =0) {
                ERROR("setgroups failed: %s\n", strerror(errno));
                _exit(127); }}if (svc->uid) {
            if(setuid(svc->uid) ! =0) {
                ERROR("setuid failed: %s\n", strerror(errno));
                _exit(127); }}if (svc->seclabel) {
            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
                _exit(127); }}if(! dynamic_args) {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] = NULL;
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
        }
        _exit(127);
    }

    freecon(scon);

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

    svc->time_started = gettime();
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;

    if((svc->flags & SVC_EXEC) ! =0) {
        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting... \n",
             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
             svc->seclabel ? : "default");
        waiting_for_exec = true;
    }

    svc->NotifyStateChange("running");
}
Copy the code

At this point the Zygote service starts and zygote will run in a child whose parent is, of course, the Init process. RegisterZygoteSocket function in ZygoteInit. This function registers a server socket. This socket is actually created in the service_start method just described. The registerZygoteSocket function retrieves the socket descriptor from the environment variable that was written to the publish_socket.

system/core/init/init.cpp

static void publish_socket(const char *name, int fd)
{
    char key[64] = ANDROID_SOCKET_ENV_PREFIX;
    char val[64];

    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
            name,
            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
    snprintf(val, sizeof(val), "%d", fd);
    // Write environment variables
    add_environment(key, val);

    /* make sure we don't close-on-exec */
    fcntl(fd, F_SETFD, 0);
}
Copy the code

ANDROID_SOCKET_ENV_PREFIX is defined in the sockets. H header. It is equal to the string “ANDROID_SOCKET_”, which means that the key we write to the environment variable is the string “ANDROID_SOCKET_zygote” with a value of fd. This corresponds exactly to the registerZygoteSocket function fetching the socket descriptor from an environment variable.

system/core/include/cutils/sockets.h

#define ANDROID_SOCKET_ENV_PREFIX	"ANDROID_SOCKET_"
Copy the code

To summarize, simply draw a sequence diagram.