Android启动(一)

Android的内核是linux,因此,此时讨论的其实是linux的启动过程,虽然这并不是纯粹的linux启动过程

此次讨论的源码基于Android4.1.1,主要涉及到的文件是init.c,init_parser.c,init.rc

  Android的开机就是不断的启动必要的系统进程服务的过程,而这些大部分进程都被写死在**init.rc**的配置文件中,那么由哪个进程来读取配置文件?

这个重任就落在init进程身上了,它是Android启动的第一个进程,主要任务就是读取配置文件,然开启其他进程。

  open_devnull_stdio();//重定向标准输出输入
    klog_init();//创建设备文件节点,写入该节点的均以printk输出
    property_init();//初始化系统属性共享内存区域
    //获取硬件平台名称及版本。若没在cmdline中提供的话,
    get_hardware_name(hardware, &revision);//将从/proc/cpuinfo中的Hardware获取
    //读出内核启动参数中的配置项的值
    process_kernel_cmdline();
  ...
  ...
 init_parse_config_file("/init.rc");//遍历解析init.rc文件!!!

而init_parse_config_file()在init_parser.c文件中,具体函数如下

//fn是.rc文件的名称
int init_parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0);//将文件内容读到内存中
    if (!data) return -1;

    parse_config(fn, data);//遍历解析.rc文件
    DUMP();
    return 0;
}

parse_config()函数关键部分如下:


    for (;;) {
        switch (next_token(&state)) {
        case T_EOF:
            state.parse_line(&state, 0, 0);
            goto parser_done;
        case T_NEWLINE:
            state.line++;
            if (nargs) {
                int kw = lookup_keyword(args[0]);//分析新的一行的第一个字符属于何种关键字
                if (kw_is(kw, SECTION)) {//若是Section,这部分会关联到keyword.h的头文件
                    state.parse_line(&state, 0, 0);//第一次为空操作
                    parse_new_section(&state, kw, nargs, args);//解析Section的第一行,并为state.parse_line赋值
                } else {
                    state.parse_line(&state, nargs, args);//下个循环则执行到此行,调用上面的赋值
                }
                nargs = 0;
            }
            break;
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }

init_parser.c文件中包含了keyword.h头文件,这个头文件中定义了一种数据结构,方便查找和读取init.rc文件的数据(有兴趣的人可以看看)。而parse_new_section(&state, kw, nargs, args);函数则是开始解析配置文件中的数据了,源码如下:

void parse_new_section(struct parse_state *state, int kw,
                       int nargs, char **args)
{
    printf("[ %s %s ]\n", args[0],
           nargs > 1 ? args[1] : "");
  ........
  .........
    case K_service://遇到服务Section,下行将返回service结构体变量指针
        state->context = parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_service;//指定遍历状态机的行遍历函数
            return;
        }
        break;
......
......

这里主要启动了两个进程Runtime和zygote进程。但是parse_service函数并不启动进程,只是“搭架子”,完成一个service结构体指针,然后添加到一个列表中。但是具体的填充工作是由parse_line_service()函数完成。最后得到一个复杂的,填充内容的结构体指针。但是,进程还是没有启动,init进程怎么启动zygote等进程呢?这是最关键的部分,而且最关键的是,我找不到啊!!!

问题在这里似乎已经走进了死胡同,结果,我尝试寻找fork函数的时候,却碰巧找到了整个init的C文件中唯一一个fork函数,处在service_start()这个函数中

好的,现在先不管init进程什么时候启动了这个service_start函数,先进入函数来看

void service_start(struct service *svc, const char *dynamic_args)

显然,svc指针就是之前构架的那个service结构体指针。

......
......
pid = fork();

    if (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)
            add_environment(ei->name, ei->value);
.....
....

这段程序fork了一个子进程,并通过 add_environment()函数添加了一些环境变量,add_environment()函数最终把环境变量放到ENV数组中。到这里,即使已经fork了其实还没有真正启动进程,因为fork是复制进程。往下看

if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }

exec系列的系统调用,是将复制的进程替换为新的进程,可以说,某种程度上,fork和exec是配合使用的,对于上面那段代码,args是字符串数组,读取自init.rc中,原本args[0]="service",agr[1]='zygote'(目前只讨论启动zygote的情况),但是当复制到svc结构体上args数组的时候,偏移了两个单位,即args[0]=args[2],那么此时args[0]="/system/bin/app_process"
init.rc文件中zygote部分是这么写的:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

所以有关于args字符串数组其实一目了然。

好了,那么fork复制出来的进程在经过execve()之后,传入“/system/bin/app_process”,就会去启动这个目录下的程序了,原先有init进程复制出来的一模一样的子进程就变成zygote进程了!!!!

接下来,我们在追问,是谁启动了service_start这个函数?

经过往上追踪,发现msg_start调用了service_start函数,然后handle_control_message又调用了msg_start函数,所以最终锁定在
handle_control_message()函数上


void handle_control_message(const char *msg, const char *arg)
{
    if (!strcmp(msg,"start")) {
        msg_start(arg);
    } else if (!strcmp(msg,"stop")) {
        msg_stop(arg);
    } else if (!strcmp(msg,"restart")) {
        msg_stop(arg);
        msg_start(arg);
    } else {
        ERROR("unknown control msg '%s'\n", msg);
    }
}

说实话,目前找不到系统是如何调用handle_control_message函数的(知道的同学欢迎交流)。看起来感觉有点想handle机制,通过一个消息队列的方式(之前在parse_service函数中讲到过,搭出service结构体的架子之后添加到一个列表中)然后取出消息,调用函数。。。。

你可能感兴趣的:(Android启动(一))