Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android啟動流程分析- action的執行和service的啟動

Android啟動流程分析- action的執行和service的啟動

編輯:關於Android編程

 

在前面的文章分析完init.rc的解析以後,我們知道現在action按照init.c的main函數中的秩序,維護了一條qlist的鏈表,listnode為action_qlist service也維護了一條鏈表,listnode為service_list。 那麼,在android的啟動過程中,action和service是如何被啟動的呢? 我們接下來再回到我們一直分析的init.c的main函數中來看一下。 在main函數中,完成前面的解析工作之後,我們分析到了一個死循環,來看一下:
    for(;;) {
        int nr, i, timeout = -1;
        execute_one_command();
        restart_processes();
        .....
 }
這個for循環的處理有很多,但是我們現在暫時關心的只有這短短的兩個函數。
        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. 來看一下action_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了。 來看一下下面的這兩個函數:
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之後,service是怎麼啟動的呢? 我們再去init.rc裡面一探究竟。
on boot                                                                                                                                                                                                                                      ....                                                                                                                                                                                                                                         class_start core
on nonencrypted
    class_start main
    class_start late_start
我們看到在action裡面,會有一些commands是class_start, 而後面跟的參數,好像與我們service的class name 是一致的。
再回到init.rc裡面看看service的部分呢?
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;
}
這個函數的實現很簡單,僅僅是傳遞調用了service_for_each_class, 並且在傳遞service name的時候,多傳遞了一個參數為service_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;
    }
}
如果這個service被設置為disabled的話,就不會被啟動,如果沒有設置的話,我們會去啟動這個service。 這裡需要注意的是,在我們調用service_start的時候,我們會去將第二個形參置為NULL。 在service_start的時候,這個函數很長很場,但是可以根據注釋,將其分為三個階段。
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] = '';
            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進程的分析,估計這十篇文章可以基本概括了init進程啟動過程中的每一個細節。 但是,這樣是不夠的,我們需要接下來繼續看一下,android系統啟動流程所繼續的其他部分。 接下來,也就是我們啟動過程中常見的zygote了!











  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved