Android Start-Up Process Analysis (10) Action execution and service start-up

Source: Internet
Author: User
Tags strcmp

#############################################

This article is extremely ice original, reproduced please indicate the source

#############################################

After analyzing the analysis of init.rc in the previous article, we know now that action follows the order of INIT.C's main function, maintaining a qlist linked list, listnode for action_qlist The service also maintains a linked list, ListNode for service_list. So how did action and service get started during Android startup? let's go back to the main function of the init.c we've been analyzing. in the main function, after completing the previous parsing work, we analyzed a dead loop and looked at:
    for (;;) {        int nr, I, timeout =-1;        Execute_one_command ();        Restart_processes ();        ..... }
There is a lot of processing for this for loop, but the only thing we care about now is this short two function.
        Execute_one_command ();
        Restart_processes ();
First we analyze the Execute_one_command function and look at the name of the function, we can understand the function. The function is to execute a command. Consider the implementation of this function:
void Execute_one_command (void) {int ret, I;    Char cmd_str[256] = ""; if (!cur_action | |!cur_command | | is_last_command (cur_action, Cur_command)) {//In the first boot, because all are null, so you can definitely enter this judgment,        If it is not the first time, because getting cur_action or cur_command is null, and if the command is the last command of the current action, it will go to the following judgment. Cur_action = Action_remove_queue_head ();  Get action from Action_queue in order Cur_command = NULL;        After getting the new action, set the Cur_command to null if (!cur_action)////returns return if there is no action;        INFO ("Processing action%p (%s) \ n", cur_action, Cur_action->name); Cur_command = Get_first_command (cur_action);  If it is a new action, it will go to this step to get first command} else {Cur_command = Get_next_command (cur_action, Cur_command);     Still in the internal linked list of actions, if there is still no command to be obtained, then the next command will be obtained.    } if (!cur_command)//If the command being obtained is empty, it will return, conversely, continue to return; ret = Cur_command->func (Cur_command->nargs, Cur_command->args); This command is called by the Func Zone execution, the number of arguments executed is Nargs, the command is the args if (kLog_get_level () >= klog_info_level) {//log print for (i = 0; i < cur_command->nargs; i++) {s            Trlcat (Cmd_str, cur_command->args[i], sizeof (CMD_STR));            if (I < cur_command->nargs-1) {Strlcat (Cmd_str, "", sizeof (CMD_STR));  }} INFO ("command '%s ' action=%s status=%d (%s:%d) \ n", Cmd_str, cur_action? cur_action->name    : ", ret, Cur_command->filename, cur_command->line); }}
In fact, this logic is better understood, we have to focus on the analysis of only how to get the action and command. Take a look at the Action_remove_queue_head function:
struct action *action_remove_queue_head (void) {    if (List_empty (&action_queue)) {// First, let's determine if the currently pending action is NULL, that is, if the action is not executed        return 0;    } else {        struct ListNode *node = List_head (& Action_queue); If there are still non-executed queues, point node to the Action_queue's head pointer        struct action *act = node_to_item (node, struct action, qlist); Remove Action        list_remove (node);   Remove        list_init (node)   from the list of the entire action _queue. After you delete this node, point to yourself to avoid the wild pointers for security reasons.        return Act;//Returns the action already found    }}
We can see that we are actually taking every structure from the action_queue. After getting the action? Take command from action. Take a look at these two functions:
The static struct command *get_first_command (struct action *act)  //looks for its first command from a actoin, so just pass the action {    struct ListNode *node;      node = List_head (&act->commands); The structure of the commands that points node to action if    (!node | | list_empty (&act->commands))  //If this node does not exist, or the commands struct of this action is empty, it returns null return null        ;    Return Node_to_item (node, struct command, clist); Returns the first node}static struct command *get_next_command (struct action *act, struct command *cmd)//Returns the next commands of the current command{
   struct ListNode *node;     node = cmd->clist.next;  The pointer moves backwards next if    (!node)  //If not present, returns null        return null;    if (node = = &act->commands)//If the node is already a head node, return null return        null;    Return Node_to_item (node, struct command, clist); Return to next node}
After the command is obtained, we will call the command method:
ret = Cur_command->func (Cur_command->nargs, Cur_command->args);
To execute every func within the command. But, strangely enough, how did the service start after the commands was executed? Let's go init.rc inside.
<span style= "Color:rgb (51, 51, 51); font-family:arial; font-size:14px; line-height:26px; BACKGROUND-COLOR:RGB (240, 240, 240); " >on boot                                                                                                                                                                                                                                      ....                                                                                                                                                                                                                                         Class_start core</span>on nonencrypted    class_start main    class_start Late_start
We see in action that there are some commands that are class_start, and the arguments behind them seem to be consistent with the class name of our service.
Go back to init.rc and look at the service part?
Service ADBD/SBIN/ADBD--ROOT_SECLABEL=U:R:SU:S0    class core    socket ADBD Stream 660 system system    disabled< C3/>seclabel u:r:adbd:s0# adbd on @ Boot in Emulatoron property:ro.kernel.qemu=1    start Adbdservice lmkd/system/bin/ LMKD    class core    critical    socket LMKD seqpacket 0660 system Systemservice servicemanager/system/bin/ ServiceManager    class Core    User System    Group system    critical    onrestart restart HEALTHD    Onrestart Restart zygote    onrestart Restart media    onrestart Restart Surfaceflinger onrestart    restart DRM
From inside the keywords, we found the corresponding function:
./init/keywords.h:53:    KEYWORD (Class_start, COMMAND, 1, Do_class_start)
Take a look at the implementation of Do_class_start:
int Do_class_start (int nargs, char **args) {/        * Starting a class does not start services         * which is explicitly dis abled.  They must         * be started individually.         *    /Service_for_each_class (args[1], service_start_if_not_disabled);    return 0;}
The implementation of this function is simple, just pass the call to Service_for_each_class, and in passing the service name, the more passed a parameter is service_start_if_not_disable.
void Service_for_each_class (const char *classname,                            Void (*func) (struct service *svc)) {    struct ListNode *node;< C2/>struct service *svc;    List_for_each (node, &service_list) {  //iterates over the structure of the service, which is not duplicated, because the name of the service has been processed        during parsing, if there is a duplicate. svc = node_to_item (node, struct service, slist); Remove each structure from the slist if        (!strcmp (Svc->classname, classname)) {  //If the name is a match, it will enter this judgment            func (SVC);  Executes the service_start_if_not_disable, and passes the current service struct to the Body        }}    }
The next thing to do isService_start_if_not_disable, let's take a look at the specific implementation:
static void service_start_if_not_disabled (struct service *svc) {    if (!) ( Svc->flags & svc_disabled) {        Service_start (SVC, NULL);    } else {        svc->flags |= svc_disabled_ START;    }}
If the service is set to disabled, it will not be started, and if it is not set, we will start the service. It is important to note that when we call Service_start, we go to the second parameter to NULL. In Service_start, this function is very long, but it can be divided into three stages according to the annotation.
void Service_start (struct service *svc, const char *dynamic_args) {///****************************** start service first    A phase struct stat s;    pid_t pid;    int needs_console;    int n;    char *scon = NULL;        int RC;          /* Starting a service removes it from the disabled or reset * State and immediately takes it out of the restarting * State if it is in there */Svc->flags &= (~ (svc_disabled| Svc_restarting| Svc_reset| Svc_restart| Svc_disabled_start));        The service is about to be started, removed from the status of disable or reset and set to re-run status svc->time_started = 0;  /* Running processes require no additional work – if * they ' re in the process of exiting, we ' ve ensured * That they'll immediately restart on exit, unless * they is ONESHOT * * if (Svc->flags & Svc_r    unning) {//If the service is still running, return return; } 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 }//If this service's flags are the initial console, but this has already started, it will set the current flags to Disabled if (stat (svc->args[0], &s)! = 0) {//If you want to execute        The command for start of this service does not exist, return error error ("Cannot find '%s ', disabling '%s ' \ n", Svc->args[0], svc->name);        Svc->flags |= svc_disabled;    Return } if ((!) ( Svc->flags & Svc_oneshot)) && Dynamic_args) {//Because Dynamic_args is null, this does not enter this judgment ERROR ("service '%s        ' must is one-shot to use dynamic args, disabling\n ", svc->args[0]);        Svc->flags |= svc_disabled;    Return }//*********************************************************** Here we can think of the second phase, SELinux is information security-related operations, this way we ignore 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]);            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 }}}//***************************************** SELinux operation end, go to the third stage NOTICE ("Starting '%s ' \ n", Svc-&gt    ; name);   PID = fork ();        Fork a self-process, that is all service started from Init.rc, is a subprocess if (pid = = 0) {//pid = 0, enter into the sub-process of the struct socketinfo *si;        struct Svcenvinfo *ei;        Char tmp[32];        int FD, SZ;        Umask (077);  if (properties_inited ()) {Get_property_workspace (&AMP;FD, &sz);            Get information about the attribute storage space and add it to the environment variable sprintf (tmp, "%d,%d", DUP (FD), SZ);        Add_environment ("Android_property_workspace", TMP); } for (ei = svc->envvars; ei; ei = ei->next)//Add the service self-declared env to the environment variable add_environment (ei->n        Ame, Ei->value);                    for (si = svc->sockets; si; si = si->next) {//socket int socket_type = SET according to socket info = ( !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, si-&            Gt;socketcon?: Scon);            if (s >= 0) {Publish_socket (si->name, s);        }} freecon (Scon);        Scon = NULL; if (svc->ioprio_class! = Ioschedclass_none) {if (Android_set_ioprio (Getpid (), Svc->ioprio_class, Svc-&gt ; ioprio_pri) {ERROR ("Failed to set pid%d Ioprio =%d,%d:%s\n", Getpid (), Svc->i            Oprio_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 ", Env[n]);    } #endif setpgid (0, Getpid ()); /* As requested, set our GID, supplemental Gids, and UID */if (Svc->gid) {//Set GID if (Setgid (svc-&                Gt;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) {//Set UID if (setuid (svc->uid)! = 0) {ERROR ("Setuid FAI                LED:%s\n ", Strerror (errno));            _exit (127); }} if (Svc->seclabel) {if (is_selinux_enabled () > 0 && setexeccon (svc->seclabe                L) < 0) {ERROR ("Cannot setexeccon ('%s '):%s\n", Svc->seclabel, Strerror (errno));            _exit (127); }} if (!dynamic_args) {//Because Dynamic_args is set to NULL, we are sure to enter this judgment when we first start from init.rc. if (Execve (Svc->args[0], (char**) Svc->args, (char**) ENV) < 0) {//!!!                Execution of the current service's start-up command, that is, from this side, we can understand that from the init process, like kernel execution init, to execute each service corresponding to the start function!            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] = ' + ';        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 (properties_inited ()) notify_service_state (Svc->name, "Running");}
Finally, the analysis of the long init process is over, and it is estimated that these 10 articles can basically summarize every detail of the INIT process during startup. However, this is not enough and we need to continue to look at the other parts of the Android system that continue to start the process. Next, it is the common zygote that we start the process!











Android Start-Up Process Analysis (10) Action execution and service start-up

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.