热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android源代码解析系列(一):init.c文件详解

本文详细解析了Android系统启动过程中的核心文件`init.c`,探讨了其在系统初始化阶段的关键作用。通过对`init.c`的源代码进行深入分析,揭示了其如何管理进程、解析配置文件以及执行系统启动脚本。此外,文章还介绍了`init`进程的生命周期及其与内核的交互方式,为开发者提供了深入了解Android启动机制的宝贵资料。

                                                                                              init.c源码跟踪

      工具:Source Insight

      最好也打开源码,一步一步的跟,并仔细看后面的注释。


一、linux内核起来后,android的第一个用户进程:init
   1.那么来看看init.c中的main函数,它做了什么?
   Init.c system\core\Init打开source Insigt
   可以看到:
    ...
    open_devnull_stdio();
    log_init();
    INFO("reading config file\n");
    parse_config_file("/init.rc");
    ...
   2. 现在主要分析:parse_config_file("/init.rc");这个函数
    跟进去:int parse_config_file(const char *fn)
            {
                char *data;
                data = read_file(fn, 0);/*读取配置文件内容,这个文件就是init.c*/
                if (!data) return -1;
           
                parse_config(fn, data);/*调用parse_config做真正的解析*/
                DUMP();
                return 0;
            }
    3.看parse_config到底什么解析:
              state.parse_line = parse_line_no_op;             /*设置解析函数,                        
                for (;;) {                                不同的内容用不同的解析函数*/
                   switch (next_token(&state)) {
                   case T_EOF:
                       state.parse_line(&state, 0, 0);
                       return;
                   case T_NEWLINE:
                       if (nargs) {
                           int kw = lookup_keyword(args[0]);
                           if (kw_is(kw, SECTION)) {                       /*判断关键字类型是不是SECTION*/
                               state.parse_line(&state, 0, 0);
                               parse_new_section(&state, kw, nargs, args);  /*是的话解析新的SECTION*/
                           } else {
                               state.parse_line(&state, nargs, args);
                           }
                           nargs = 0;
                       }
                       break;
                   case T_TEXT:
                       if (nargs                            args[nargs++] = state.text;
                       }
                       break;
                   }
               }
     可见,这个函数是以一个一个Section进行解析的,而且不同的section使用不同的解析函数。
    4. 那么,如何判断那些属于一个section呢?
       1) 先去看看keyword.h中:
          #ifndef KEYWORD                           
          int do_class_start(int nargs, char **args);         /*声明一些函数,不同的内容用不同的解析函数*/
          int do_class_stop(int nargs, char **args);        
          ...
          ...
          #define __MAKE_KEYWORD_ENUM__                                      
          #define KEYWORD(symbol, flags, nargs, func) K_##symbol,   /*定义KEYWORD宏,虽然有四个参数,但这里只用一个 (得到K_symbol)*/
          enum {                                                    /*定义了各个关键字的枚举值*/                                      
          #endif                                                
              KEYWORD(capability,  OPTION,  0, 0)                  /*宏展开为:K_capability*/   
              KEYWORD(class,       OPTION,  0, 0)               
              KEYWORD(class_start, COMMAND, 1, do_class_start)  
          ....
          ....
          #ifdef __MAKE_KEYWORD_ENUM__
              KEYWORD_COUNT,         
          };                         
          #undef __MAKE_KEYWORD_ENUM__
          #undef KEYWORD                                             /*取消宏定义*/          
          #endif     
         
         
    2) 再看看,parser.c中:
     /*第一次包含*/ #include "keywords.h"                            /*第一次包含得到了一个枚举定义。*/
                                             /*接下来重新定义KEYWORD宏,*/
                    #define KEYWORD(symbol, flags, nargs, func) \
                        [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
                       
                   
                    struct {
                        const char *name; /*关键字的名称*/
                        int (*func)(int nargs, char **args);/*对应关键字的处理函数*/
                        unsigned char nargs;  /*参数个数,每个关键字的参数个数是固定的*/
                        unsigned char flags; /*关键字的属性有三种:COMMEND,OPTION,SECTION,
                           其中COMMAND有对应的处理函数*/
                    } keyword_info[KEYWORD_COUNT] = {
                        [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
    /*第二次包含*/  #include "keywords.h"
      
                    };
                  
                   
                    #undef KEYWORD
                   
                    #define kw_is(kw, type) (keyword_info[kw].flags & (type))
                    #define kw_name(kw) (keyword_info[kw].name)
                    #define kw_func(kw) (keyword_info[kw].func)
                    #define kw_nargs(kw) (keyword_info[kw].nargs)    
     从中可以发现,包含了两次头文件,第一次包含得到了一个枚举定义。
     
     将#include "keywords.h"替换,就可以很清楚的知道什么回事了:
                    第一步为:(也就是第一次#include "keywords.h"之前)
                                KEYWORD(capability,  OPTION,  0, 0) /*宏展开为:K_capability*/
                                KEYWORD(class,       OPTION,  0, 0)
                                KEYWORD(class_start, COMMAND, 1, do_class_start)
                                KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
                                ......
                    第二步:第一次替换成:   (第一次#include "keywords.h"之后)
                                K_capability
                                K_class
                                K_class_start
                                K_class_stop
                               
                    第三步:重新定义KEYWORD
                          #define KEYWORD(symbol, flags, nargs, func) \
                                  [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
                    第四步:第二次#include "keywords.h"   
                            struct {
                                const char *name; /*关键字的名称*/
                                int (*func)(int nargs, char **args);/*对应关键字的处理函数*/
                                unsigned char nargs;  /*参数个数,每个关键字的参数个数是固定的*/
                                unsigned char flags; /*关键字的属性有三种:COMMEND,OPTION,SECTION,
                                   其中COMMAND有对应的处理函数*/
                            } keyword_info[KEYWORD_COUNT] = {
                                [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
                             
                                [K_capability] = "capability",func,nargs+1,flags,},
                                [K_class] = "class",func,nargs+1,flags,},
                                .....
                              }; 
                             
             回到第四点的问题:我们在keywords.h中发现只有
                 KEYWORD(on,          SECTION, 0, 0)
                 KEYWORD(service,     SECTION, 0, 0)
             这两的中含有关键字SECTION。
            
     5.我们进init.rc中
    
                on init     #on 关键字标志一个section,对应的名字为“init”
                            # 下面所有的内容都属于这个section,直到下一个section开始时
                sysclktz 0
               
                loglevel 3
               
                # setup the global environment
                    export PATH /sbin:/system/sbin:/system/bin:/system/xbin
                    export LD_LIBRARY_PATH /system/lib
                    export ANDROID_BOOTLOGO 1       #根据keyword.h可知,export表示一个command
                    export ANDROID_ROOT /system
                    export ANDROID_ASSETS /system/app
                    export ANDROID_DATA /data
                 ....
                 ....           
                 on boot
                 ...
                 ...
                 class_start default
                
                 #class_start也是一个command,对应函数为do_class_start
              #keywords.h--->KEYWORD(class_start, COMMAND, 1, do_class_start)
              ...
              ...
                 service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
                    socket zygote stream 666
                    onrestart write /sys/android_power/request_state wake
                    onrestart write /sys/power/state on
                    onrestart restart media
  
   6. 解析service
             service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
                    socket zygote stream 666  #socket是OPTION
                    #下面的onrestart是OPTION,而write和restart是COMMAND。
                    onrestart write /sys/android_power/request_state wake
                    onrestart write /sys/power/state on
                    onrestart restart media
      解析section的入口函数是parser.c---->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] : "");
                                    switch(kw) {
                                    case K_service:
                                        state->cOntext= parse_service(state, nargs, args);
                                        if (state->context) {
                                            state->parse_line = parse_line_service;
                                            return;
                                        }
                                        break;
                                    case K_on:
                                        state->cOntext= parse_action(state, nargs, args);
                                        if (state->context) {
                                            state->parse_line = parse_line_action;
                                            return;
                                        }
                                        break;
                                    }
                                    state->parse_line = parse_line_no_op;
                                }


推荐阅读
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 本文介绍如何使用Objective-C结合dispatch库进行并发编程,以提高素数计数任务的效率。通过对比纯C代码与引入并发机制后的代码,展示dispatch库的强大功能。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
  • 在前两篇文章中,我们探讨了 ControllerDescriptor 和 ActionDescriptor 这两个描述对象,分别对应控制器和操作方法。本文将基于 MVC3 源码进一步分析 ParameterDescriptor,即用于描述 Action 方法参数的对象,并详细介绍其工作原理。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 本文详细介绍了Java中org.w3c.dom.Text类的splitText()方法,通过多个代码示例展示了其实际应用。该方法用于将文本节点在指定位置拆分为两个节点,并保持在文档树中。 ... [详细]
  • 本文探讨了Hive中内部表和外部表的区别及其在HDFS上的路径映射,详细解释了两者的创建、加载及删除操作,并提供了查看表详细信息的方法。通过对比这两种表类型,帮助读者理解如何更好地管理和保护数据。 ... [详细]
  • 深入理解Tornado模板系统
    本文详细介绍了Tornado框架中模板系统的使用方法。Tornado自带的轻量级、高效且灵活的模板语言位于tornado.template模块,支持嵌入Python代码片段,帮助开发者快速构建动态网页。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • CentOS7源码编译安装MySQL5.6
    2019独角兽企业重金招聘Python工程师标准一、先在cmake官网下个最新的cmake源码包cmake官网:https:www.cmake.org如此时最新 ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
author-avatar
许先生不会再想过去的事观_307
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有