Android起動プロセス分析(十)actionの実行とサービスの起動


#############################################
本文は極度の冷たい氷のオリジナルで、転載して出典を明記してください
#############################################
前の文章で分析した.rcの解析の後、私たちは今actionがinitに従っていることを知っています.cのmain関数の秩序はqlistのチェーンテーブルを維持し、listnodeはaction_である.qlist
サービスもチェーンテーブルを維持し、listnodeはサービス_list.
では、androidの起動中にactionとserviceはどのように起動されたのでしょうか.
私たちはこれから分析してきたinitに戻ります.cのmain関数で見てみましょう.
main関数では、前の解析作業が完了した後、デッドサイクルを解析しました.
    for(;;) {
        int nr, i, timeout = -1;
        execute_one_command();
        restart_processes();
        .....
 }
というforループの処理はたくさんありますが、私たちが今一時的に関心を持っているのはこの短い2つの関数だけです.
        execute_one_command();
        restart_processes();

まずexecute_を分析しますone_command関数は、関数の名前を見て、この関数の機能を理解することができます.
この関数はcommandを実行することですか.
この関数の実装を見てみましょう.
void execute_one_command(void)
{
    int ret, i;
    char cmd_str[256] = "";

    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) { //         ,    NULL,            ,         ,    cur_action  cur_command  null,      command   action     command  ,           。
        cur_action = action_remove_queue_head(); //     action action_queue 
        cur_command = NULL;  //      action  , cur_command   null
        if (!cur_action)  //     action ,   
            return;
        INFO("processing action %p (%s)
", cur_action, cur_action->name); cur_command = get_first_command(cur_action); // action , first command } else { cur_command = get_next_command(cur_action, cur_command); // action , command , command。 } if (!cur_command) // command , , , return; ret = cur_command->func(cur_command->nargs, cur_command->args); // command func , nargs, args if (klog_get_level() >= KLOG_INFO_LEVEL) { // Log for (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)); } } INFO("command '%s' action=%s status=%d (%s:%d)
", cmd_str, cur_action ? cur_action->name : "", ret, cur_command->filename, cur_command->line); } }
実はこの論理は理解しやすいので、actionとcommandをどのように取得するかに重点を置いて分析します.
アクションを見てみましょうremove_queue_headという関数:
struct action *action_remove_queue_head(void)
{
    if (list_empty(&action_queue)) { //              action     null,     action     
        return 0;
    } else {
        struct listnode *node = list_head(&action_queue); //               ,  node    action_queue    
        struct action *act = node_to_item(node, struct action, qlist); //   action
        list_remove(node);   //         action _queue      
        list_init(node);   //        ,      , node      ,        。
        return act; //         action
    }
}
実際にはactionからqueueでは各構造体を取ります.
actionを手に入れた後は?もうすぐactionからcommandを取りに行きます.
次の2つの関数を見てみましょう.
static struct command *get_first_command(struct action *act)  //    actoin        command,      action  
{
    struct listnode *node;  
    node = list_head(&act->commands); //  node  action commands    
    if (!node || list_empty(&act->commands))  //          ,    action commands     ,   null
        return NULL;

    return node_to_item(node, struct command, clist); //        
}

static struct command *get_next_command(struct action *act, struct command *cmd) //     commands    command
{
    struct listnode *node; 
    node = cmd->clist.next;        next
    if (!node)  //      ,   null
        return NULL;
    if (node == &act->commands) //              ,   null
        return NULL;

    return node_to_item(node, struct command, clist); //   next  
}
commandを取得した後、commandのメソッドを呼び出します.
 ret = cur_command->func(cur_command->nargs, cur_command->args);
commandの各funcを実行します.
しかし、非常に奇妙なことに、commandsを実行した後、サービスはどのように起動したのでしょうか.
また行こうrcの中で真相を探る.
<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
actionの中にはclassというcommandsがありますstart、後ろのパラメータは、私たちのサービスのclass nameと一致しているようです.
また戻ってrcの中でサービスの部分を見てみましょうか?
service adbd /sbin/adbd --root_seclabel=u:r:su:s0
    class core
    socket adbd stream 660 system system
    disabled
    seclabel u:r:adbd:s0

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

service lmkd /system/bin/lmkd
    class core
    critical
    socket lmkd seqpacket 0660 system system

service 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
keywordsから、対応するfunctionを見つけました.
./init/keywords.h:53:    KEYWORD(class_start, COMMAND, 1, do_class_start)
do_を見てみましょうclass_startの実装:
int do_class_start(int nargs, char **args)
{
        /* Starting a class does not start services
         * which are explicitly disabled.  They must
         * be started individually.
         */
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}
この関数の実装は簡単で、サービスを呼び出しただけです.for_each_classは、サービス名を渡すときに、サービス_というパラメータを多く渡します.start_if_not_disable.
void service_for_each_class(const char *classname,
                            void (*func)(struct service *svc))
{
    struct listnode *node;
    struct service *svc;
    list_for_each(node, &service_list) {  //   service    ,        ,  service name        ,            
        svc = node_to_item(node, struct service, slist); //  slist         
        if (!strcmp(svc->classname, classname)) {  //          ,        
            func(svc);  //   service_start_if_not_disable,       service        
        }
    }
}
次に実行するのは
service_start_if_not_disableでは、具体的な実装を見てみましょう.
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;
    }
}
このサービスがdisabledに設定されていれば起動されず、設定されていなければこのサービスを起動します.
ここで注意しなければならないのは、サービスを呼び出すことです.startの時、私たちは2番目の形をNULLにします.
サービス_startの場合,この関数は長いフィールドであるが,注釈に基づいて3つの段階に分けることができる.
void service_start(struct service *svc, const char *dynamic_args)
{   /// ******************************  start service       
    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 was in there
         */
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); //   service     ,   disable reset       ,          
    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) {  //     service        , return
        return;
    }

    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
    if (needs_console && (!have_console)) {
        ERROR("service '%s' requires console
", svc->name); svc->flags |= SVC_DISABLED; return; } // service flags console, , flags disabled if (stat(svc->args[0], &s) != 0) { // service start command , error ERROR("cannot find '%s', disabling '%s'
", svc->args[0], svc->name); svc->flags |= SVC_DISABLED; return; } if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) { // dynamic_args null, ERROR("service '%s' must be one-shot to use dynamic args, disabling
", svc->args[0]); svc->flags |= SVC_DISABLED; return; } // *********************************************************** ,selinux , if (is_selinux_enabled() > 0) { if (svc->seclabel) { scon = strdup(svc->seclabel); if (!scon) { ERROR("Out of memory while starting '%s'
", svc->name); return; } } else { char *mycon = NULL, *fcon = NULL; INFO("computing context for service '%s'
", svc->args[0]); rc = getcon(&mycon); if (rc < 0) { ERROR("could not get context while starting '%s'
", svc->name); return; } rc = getfilecon(svc->args[0], &fcon); if (rc < 0) { ERROR("could not get context while starting '%s'
", 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!
", svc->name); } freecon(mycon); freecon(fcon); if (rc < 0) { ERROR("could not get context while starting '%s'
", svc->name); return; } } } // ***************************************** selinux , NOTICE("starting '%s'
", svc->name); pid = fork(); // fork , init.rc service, if (pid == 0) { // pid = 0, struct socketinfo *si; struct svcenvinfo *ei; char tmp[32]; int fd, sz; umask(077); 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) // service env add_environment(ei->name, ei->value); for (si = svc->sockets; si; si = si->next) { // socket info socket 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, si->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->ioprio_pri)) { ERROR("Failed to set pid %d ioprio = %d,%d: %s
", 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, 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) { // gid if (setgid(svc->gid) != 0) { ERROR("setgid failed: %s
", strerror(errno)); _exit(127); } } if (svc->nr_supp_gids) { if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) { ERROR("setgroups failed: %s
", strerror(errno)); _exit(127); } } if (svc->uid) { // uid if (setuid(svc->uid) != 0) { ERROR("setuid failed: %s
", strerror(errno)); _exit(127); } } if (svc->seclabel) { if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) { ERROR("cannot setexeccon('%s'): %s
", svc->seclabel, strerror(errno)); _exit(127); } } if (!dynamic_args) { // dynamic_args null, init.rc , 。 if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { // !!! service , , init , kernel init , service ! ERROR("cannot execve('%s'): %s
", 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); } freecon(scon); if (pid < 0) { ERROR("failed to start '%s'
", 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"); }
はついに長いinitプロセスの分析を終了し、この10の文章はinitプロセスの起動過程のすべての詳細を基本的に概括することができると推定されている.
しかし、これでは十分ではありません.androidシステムの起動プロセスが続く他の部分を見続けなければなりません.
次は、起動中によく見られるzygoteです!