Android启动流程分析(十) action的执行和service的启动

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

本文为极度寒冰原创,转载请注明出处

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

在前面的文章分析完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)\n", 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)\n",
             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里面一探究竟。

<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里面,会有一些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\n", 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'\n", 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\n",
               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'\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的操作结束,进入到第三个阶段
    NOTICE("starting '%s'\n", 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\n",
                      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", n, svc->args[n]);
        }
        for (n = 0; ENV[n]; n++) {
            INFO("env[%d] = '%s'\n", 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\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) {  // 设置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) { // 因为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\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] = '\0';
            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");
}

终于结束了漫长的init进程的分析,估计这十篇文章可以基本概括了init进程启动过程中的每一个细节。

但是,这样是不够的,我们需要接下来继续看一下,android系统启动流程所继续的其他部分。

接下来,也就是我们启动过程中常见的zygote了!

时间: 2024-10-15 16:52:47

Android启动流程分析(十) action的执行和service的启动的相关文章

Android启动流程分析(十二) SystemServer

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# SystemServer也是系统的一个重要的守护进程,从SystemServer的进程中,我们看到了系统的各种关键的Service的启动. 另外,根据前面的zygote的分析,我们知道了systemServer在android的启动过程中是肯定要启动的. 因为在init.rc里面

Android启动流程分析(九) 解析init.rc的service

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# 在分析完解析init.rc的action之后,剩下的一部分就是解析service了. 而解析service还是需要回到parse_config里面来.根据前面的知识,我们也可以很容易的知道在关键字为section的时候,会进入到parse_new_section. 这里会先执行p

Cocos2d-x3.3RC0的Android编译Activity启动流程分析

本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXV4aWt1b18x/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" > watermark/2/text/aHR0cDov

Android -- Audio Native服务之启动流程分析(一)

Android -- Audio Native服务之启动流程分析(一) Android中的Audio系统是比较庞大.繁杂的一部分内容, 其中会涉及较多的音频编解码.多媒体制式与Android Audio HAL设备管理的知识.随着Android的发展,其所支持的音频设备也变得越来丰富,如扬声器.耳机.听筒等等:这种变化也为Android管理如此丰富的音频设备以及如何正确.合理地切换音频输出提出了更高的要求.面对如此繁杂的管理要求,我们分析Android Audio服务的历程想必也不会轻松.接下来

Android启动流程分析(一)

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# Android的启动流程绝大部分人都知道,但是大多数人都是这样描述的: Android启动,首先是启动Bootloader,然后挂载kernel,挂载完kernel之后,会启动android的init进程,init进程会去孵化Zygote,Zygote是android的重要支柱之

Android 启动流程分析

原文:https://www.jianshu.com/p/a5532ecc8377 作者曾经在高通的Android性能组工作,主要工作是优化Android Application的启动时间. APP基础理论 要想优化App启动时间, 第一步就是了解App启动进程的工作原理. 有几个基础理论: Android Application与其他移动平台有两个重大不同点: 每个Android App都在一个独立空间里,意味着其运行在一个单独的进程中,拥有自己的VM, 被系统分配一个唯一的user ID A

Android5 Zygote 与 SystemServer 启动流程分析

Android5 Zygote 与 SystemServer 启动流程分析 Android5 Zygote 与 SystemServer 启动流程分析 前言 zygote 进程 解析 zygoterc 启动 SystemServer 执行 ZygoteInitrunSelectLoop SystemServer 启动过程 Zygote 的 fork 本地方法分析 forkSystemServer ZygoteHookspreFork 创建 system_server 进程 ZygoteHooks

u-boot启动流程分析(2)_板级(board)部分

转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global data介绍以及背后的思考 5. 前置的板级初始化操作 6. u-boot的relocation 7. 后置的板级初始化操作 1. 前言 书接上文(u-boot启动流程分析(1)_平台相关部分),本文介绍u-boot启动流程中和具体版型(board)有关的部分,也即board_init_f/board_i

Uboot启动流程分析(四)

1.前言 在前面的文章Uboot启动流程分析(三)中,链接如下: https://www.cnblogs.com/Cqlismy/p/12006287.html 已经对init_sequence_f前半部分函数进行了简单分析,前半部分主要是对调试串口终端进行了初始化,以及输出了一些必要的字符串,接下来,本篇文章将对init_sequence_f后半部分函数进行分析,后半部分主要是对DRAM的内存进行分配,并对gd的相关结构体成员进行初始化,在init_sequence_f函数初始化列表中,已经执