android启动代码init.c文件分析(四)

本文深入分析Android启动过程中init.c的for循环,讲解execute_one_command函数如何处理action和command结构体,实现命令的执行。同时,介绍了restart_processes函数在服务重启中的作用,以及poll函数在检测socket变化中的应用。

/*****************************************************************

*version:android4.2

*author:冷雨

*嵌入式开发群:122879839

*****************************************************************/

 

  终于来到最后了,这是一个长长的for循环。

    for(;;) {
        int nr, i, timeout = -1;

        execute_one_command();
        restart_processes();

        if (!property_set_fd_init && get_property_set_fd() > 0) {
            ufds[fd_count].fd = get_property_set_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            property_set_fd_init = 1;
        }
        if (!signal_fd_init && get_signal_fd() > 0) {
            ufds[fd_count].fd = get_signal_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            signal_fd_init = 1;
        }
        if (!keychord_fd_init && get_keychord_fd() > 0) {
            ufds[fd_count].fd = get_keychord_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            keychord_fd_init = 1;
        }

        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }

        if (!action_queue_empty() || cur_action)
            timeout = 0;

#if BOOTCHART
        if (bootchart_count > 0) {
            if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
                timeout = BOOTCHART_POLLING_MS;
            if (bootchart_step() < 0 || --bootchart_count == 0) {
                bootchart_finish();
                bootchart_count = 0;
            }
        }
#endif

        nr = poll(ufds, fd_count, timeout);
        if (nr <= 0)
            continue;

        for (i = 0; i < fd_count; i++) {
            if (ufds[i].revents == POLLIN) {
                if (ufds[i].fd == get_property_set_fd())
                    handle_property_set_fd();
                else if (ufds[i].fd == get_keychord_fd())
                    handle_keychord();
                else if (ufds[i].fd == get_signal_fd())
                    handle_signal();
            }
        }
    }

    return 0;
}


 

  首先调用execute_one_command函数。

void execute_one_command(void)
{
    int ret;

    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;
        INFO("processing action %p (%s)\n", cur_action, cur_action->name);
        cur_command = get_first_command(cur_action);
    } else {
        cur_command = get_next_command(cur_action, cur_command);
    }

    if (!cur_command)
        return;

    ret = cur_command->func(cur_command->nargs, cur_command->args);
    INFO("command '%s' r=%d\n", cur_command->args[0], ret);
}


 

  这里涉及到两个变量,我们先贴出代码。

static struct action *cur_action = NULL;
static struct command *cur_command = NULL;


 

  由于是第一次调用这个函数,这里的cur_actioncur_command现在仍未NULL,所以第一个if里面的代码可以执行。里面首先调用了action_remove_queue_head函数。

struct action *action_remove_queue_head(void)
{
    if (list_empty(&action_queue)) {
        return 0;
    } else {
        struct listnode *node = list_head(&action_queue);
        struct action *act = node_to_item(node, struct action, qlist);
        list_remove(node);
        return act;
    }
}


 

  在这个函数中首先获得第一个action结构体,然后将这个结构体对应的节点从action_queue里面删除,最后返回这个action结构体。

  回到execute_one_command函数中,调用get_first_command去获得一个command结构体指针。看一下他的实现函数。

static struct command *get_first_command(struct action *act)
{
    struct listnode *node;
    node = list_head(&act->commands);
    if (!node || list_empty(&act->commands))
        return NULL;

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


 

  这个函数首先获得head节点,然后利用node_to_item将这个节点转化成command结构体指针。

  再次回到execute_one_command函数中,如果能够正确的获得command结构体指针,便会执行cur_command->func函数。

  这样我们知道main函数中调用execute_one_command函数的作用了。首先从action_queue链表中获得头部的action结构体,并将这个头部的action结构体从action_queue移除。如果我们是第一次使用这个action结构体的话,我们会调用get_first_command函数去获得链接在它上面的第一个command结构体,并执行这个结构体对应的函数;如果我们不是第一次使用这个action结构体的话,我们会调用get_next_command函数去获得链接在这个action结构体的下一个command结构体,并执行对应的函数。

main函数中,我们现在是处于一个for循环中。所以,每当我们在for循环中调用execute_one_command的时候,便会获得一个command结构体,并执行其对应的函数。当我们获得action结构体的最后一个command结构体后,再次调用execute_one_command函数的时候,我们就会从action_queue中去获得新的action结构体,并重复获得command结构体,执行函数的步骤,直到最终我们把action_queue链表中的所有的action结构体上的所有的command都调用一遍。

 

  我们把思维拉回到main函数中,接下来有一个restart_processes函数。这个函数是做什么的?如果我们启动的service有死掉的话,这个函数便会检测到,并且调用相应的函数去重新打开service,即实现restart功能。

static void restart_processes()
{
    process_needs_restart = 0;
    service_for_each_flags(SVC_RESTARTING,
                           restart_service_if_needed);
}
void service_for_each_flags(unsigned matchflags,
                            void (*func)(struct service *svc))
{
    struct listnode *node;
    struct service *svc;
    list_for_each(node, &service_list) {
        svc = node_to_item(node, struct service, slist);
        if (svc->flags & matchflags) {
            func(svc);
        }
    }
}


 

  注意到我们传给service_for_each_flags函数的参数中unsignedmatchflagsSVC_RESTARTING,在service_for_each_flags函数中,我们首先获得一个service结构体,然后让这个结构体的flags匹配我们传递过来的SVC_RESTARTING,如果我们这个结构体的flags恰好是SVC_RESTARTING的话,就执行我们作为参数传递过来的那个函数即restart_service_if_needed

 

  我们回到main函数中。下面是关于poll函数的使用,poll在这里用于检测socket变化的函数。看代码,如果条件满足的话便会初始化struct pollfd ufds[4]中各个成员变量。其实这里我们仅仅操作了ufds数组中的三个成员。我们先看第一个成员,如果(!property_set_fd_init && get_property_set_fd() > 0)这个条件满足的话就会进行下面的初始化。

intproperty_set_fd_init = 0;

所以!property_set_fd_init这里是1get_property_set_fd()函数是定义在system/core/init/property_service.c文件中的函数。

int get_property_set_fd()
{
    return property_set_fd;
}


 

  他返回的是property_set_fd变量,这个变量是多少?

static int property_set_fd = -1;

  这里的property_set_fd = -1是初始值,那么在什么时候这个值能够满足我们的判断条件呢。还记得前面前面执行过下面一行代码吗?

queue_builtin_action(property_service_init_action,"property_service_init");

  这段代码的作用是创建一个action结构体和一个command结构体,将这个command结构体链入了action结构体,并将property_service_init_action赋值给了command结构体的func变量。这样在main函数的for循环下找到这个action结构体后,会依次找到action结构体下面的command结构体,并调用对应的func成员函数。最终会调用到我们的这个property_service_init_action函数。我们看看这个函数的实现代码。

static int property_service_init_action(int nargs, char **args)
{
    /* read any property files on system or data and
     * fire up the property service.  This must happen
     * after the ro.foo properties are set above so
     * that /data/local.prop cannot interfere with them.
     */
    start_property_service();
    return 0;
}
void start_property_service(void)
{
    int fd;

    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
    load_override_properties();
    /* Read persistent properties after all default values have been loaded. */
    load_persistent_properties();

    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
    if(fd < 0) return;
    fcntl(fd, F_SETFD, FD_CLOEXEC);
    fcntl(fd, F_SETFL, O_NONBLOCK);

    listen(fd, 8);
    property_set_fd = fd;
}

  看到start_property_service函数的最后一行没?这里改变了property_set_fd的值!这样我们main函数下的if判断就能够通过了也就可以去初始化对应的成员变量。

  同理,当我们调用signal_init_actionkeychord_init_action函数的时候,我们会让下面的那两个if判断通过,最终这三个都会设置完毕。我们仔细看一下这里对events成员赋值都是POLLIN,这样当后期有read events可操作时就会返回。

 

  在main函数中紧跟着这三个if后面仍然是两个if结构,这两个是关于timout的设置就不看了。最后调用poll函数。如果poll能检测到socket变化,就利用一个for循环去判断发生变化的是哪一个socket。并最终调用对应的函数去处理这些变化。


 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值