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结构体的架子之后添加到一个列表中)然后取出消息,调用函数。。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容