热门标签 | 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;
                                }


推荐阅读
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 基于Linux开源VOIP系统LinPhone[四]
    ****************************************************************************************** ... [详细]
  • 为了确保iOS应用能够安全地访问网站数据,本文介绍了如何在Nginx服务器上轻松配置CertBot以实现SSL证书的自动化管理。通过这一过程,可以确保应用始终使用HTTPS协议,从而提升数据传输的安全性和可靠性。文章详细阐述了配置步骤和常见问题的解决方法,帮助读者快速上手并成功部署SSL证书。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • 字符串学习时间:1.5W(“W”周,下同)知识点checkliststrlen()函数的返回值是什么类型的?字 ... [详细]
  • 微软推出Windows Terminal Preview v0.10
    微软近期发布了Windows Terminal Preview v0.10,用户可以在微软商店或GitHub上获取这一更新。该版本在2月份发布的v0.9基础上,新增了鼠标输入和复制Pane等功能。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 在 Ubuntu 中遇到 Samba 服务器故障时,尝试卸载并重新安装 Samba 发现配置文件未重新生成。本文介绍了解决该问题的方法。 ... [详细]
  • 如何在Linux服务器上配置MySQL和Tomcat的开机自动启动
    在Linux服务器上部署Web项目时,通常需要确保MySQL和Tomcat服务能够随系统启动而自动运行。本文将详细介绍如何在Linux环境中配置MySQL和Tomcat的开机自启动,以确保服务的稳定性和可靠性。通过合理的配置,可以有效避免因服务未启动而导致的项目故障。 ... [详细]
  • 属性类 `Properties` 是 `Hashtable` 类的子类,用于存储键值对形式的数据。该类在 Java 中广泛应用于配置文件的读取与写入,支持字符串类型的键和值。通过 `Properties` 类,开发者可以方便地进行配置信息的管理,确保应用程序的灵活性和可维护性。此外,`Properties` 类还提供了加载和保存属性文件的方法,使其在实际开发中具有较高的实用价值。 ... [详细]
  • Flowable 流程图路径与节点展示:已执行节点高亮红色标记,增强可视化效果
    在Flowable流程图中,通常仅显示当前节点,而路径则需自行获取。特别是在多次驳回的情况下,节点可能会出现混乱。本文重点探讨了如何准确地展示流程图效果,包括已结束的流程和正在执行的流程。具体实现方法包括生成带有高亮红色标记的图片,以增强可视化效果,确保用户能够清晰地了解每个节点的状态。 ... [详细]
  • Android 构建基础流程详解
    Android 构建基础流程详解 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
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社区 版权所有