SuperIC社区_

标题: Android系统镜像文件的打包|过程分析(3) [打印本页]

作者: liuwei    时间: 2016-9-28 13:13
标题: Android系统镜像文件的打包|过程分析(3)
二. boot.img         

       从前面的分析可以知道,build/core/main.mk文件定义了boot.img镜像文件的依赖规则,我们可以通过执行make bootimage命令来执行。其中,bootimage是一个伪目标,它依赖于INSTALLED_BOOTIMAGE_TARGET。

       INSTALLED_BOOTIMAGE_TARGET定义在build/core/Makefile文件中,如下所示:


[plain] view plain copy



       在介绍boot.img之前,我们首先要介绍BootLoader。我们都知道,PC启动的时候,首先执行的是在BIOS上的代码,然后再由BIOS负责将Kernel加载起来执行。在嵌入式世界里,BootLoader的作用就相当于PC的BIOS。

       BootLoader的启动通常分为两个阶段。第一阶段在固态存储中(Flash)执行,负责初始化硬件,例如设置CPU工作模式、时钟频率以及初始化内存等,并且将第二阶段拷贝到RAM去准备执行。第二阶段就是在RAM中执行,因此速度会更快,主要负责建立内存映像,以及加载Kernel镜像和Ramdisk镜像。

       BootLoader的第二阶段执行完成后,就开始启动Kernel了。Kernel负责启动各个子系统,例如CPU调度子系统和内存管理子系统等等。Kernel启动完成之后,就会将Ramdisk镜像安装为根系统,并且在其中找到一个init文件,将其启动为第一个进程。init进程启动就意味着系统进入到用户空间执行了,这时候各种用户空间运行时以及守护进程就会被加载起来。最终完成整个系统的启动过程。

       BootLoader的第一阶段是固化在硬件中的,而boot.img的存在就是为BootLoader的第一阶段提供第二阶段、Kernel镜像、Ramdisk镜像,以及相应的启动参数等等。也就是说,boot.img主要包含有BootLoader的第二阶段、Kernel镜像和Ramdisk镜像。当然,BootLoader的第二阶段是可选的。当不存在BootLoader的第二阶段的时候,BootLoader的第一阶段启动完成后,就直接进入到kernel启动阶段。

       boot.img镜像的文件格式定义在system/core/mkbootimg/bootimg.h中,如下所示:


[plain] view plain copy



        它由4部分组成:boot header、kernel、ramdisk和second state。每一个部分的大小都是以页为单位的,其中,boot header描述了kernel、ramdisk、sencond stage的加载地址、大小,以及kernel启动参数等等信息。

        boot header的结构同样是定义在system/core/mkbootimg/bootimg.h中,如下所示:


[cpp] view plain copy



         各个成员变量的含义如下所示:

         magic:魔数,等于“ANDROID!”。

         kernel_size:Kernel大小,以字节为单位。

         kernel_addr:Kernel加载的物理地址。

         ramdisk_size:Ramdisk大小,以字节为单位。

         ramdisk_addr:Ramdisk加载的物理地址。

         second_size:BootLoader第二阶段的大小,以字节为单位。

         second_addr:BootLoader第二阶段加载的物理地址。

         tags_addr:Kernel启动之前,它所需要的启动参数都会填入到由tags_addr所描述的一个物理地址中去。

         unused:保留以后使用。

         page_size:页大小。

         name:产品名称。

         id:时间戳、校验码等等。

        理解了BootLoader的启动过程,以及boot.img的文件结构之后,就不难理解boot.img文件的生成过程了。

        首先检查变量TARGET_NO_KERNEL的值是否等于true。如果等于true的话,那么就表示boot.img不包含有BootLoader第二阶段和Kernel,即它等同于Ramdisk镜像。如果不等于true的话,那么就通过INSTALLED_2NDBOOTLOADER_TARGET、INSTALLED_KERNEL_TARGET和INSTALLED_RAMDISK_TARGET获得BootLoader第二阶段、Kernel和Ramdisk对应的镜像文件,以及通过BOARD_KERNEL_CMDLINE、BOARD_KERNEL_BASE和BOARD_KERNEL_PAGESIZE获得Kernel启动命令行参数、内核基地址和页大小等参数。最后根据TARGET_BOOTIMAGE_USE_EXT2的值来决定是使用genext2fs还是mkbootimg工具来生成boot.img。

        三. ramdisk.img

        从前面的分析可以知道,build/core/main.mk文件定义了ramdisk.img镜像文件的依赖规则,我们可以通过执行make ramdisk命令来执行。其中,ramdisk是一个伪目标,它依赖于INSTALLED_RAMDISK_TARGET。

        INSTALLED_RAMDISK_TARGET定义在build/core/Makefile文件中,如下所示:


[plain] view plain copy



        ALL_PREBUILT、ALL_COPIED_HEADERS、ALL_GENERATED_SOURCES和ALL_DEFAULT_INSTALLED_MODULES这几个变量的含义前面分析system.img的生成过程时已经介绍过了。因此,这里我们就很容易知道,ramdisk.img镜像实际上就是由这几个变量所描述的、保存在TARGET_ROOT_OUT目录中的文件所组成。与此相对应的是,system.img由保存在TARGET_OUT目录中的文件组成。

        TARGET_ROOT_OUT和TARGET_OUT又分别是指向什么目录呢?假设我们的编译目标产品是模拟器,那么TARGET_ROOT_OUT和TARGET_OUT对应的目录就分别为out/target/product/generic/root和out/target/product/generic/system。

        收集好对应的文件之后,就可以通过MKBOOTFS和MINIGZIP这两个变量描述的mkbootfs和minigzip工具来生成一个格式为cpio的ramdisk.img了。mkbootfs和minigzip这两个工具对应的源码分别位于system/core/cpio和external/zlib目录中。

        四. userdata.img

        userdata.img镜像描述的是Android系统的data分区,即/data目录,里面包含了用户安装的APP以及数据等等。

        从前面的分析可以知道,build/core/main.mk文件定义了userdata.img镜像文件的依赖规则,我们可以通过执行make userdataimage命令来执行。其中,userdataimage是一个伪目标,它依赖于INSTALLED_USERDATAIMAGE_TARGET。

        INSTALLED_USERDATAIMAGE_TARGET定义在build/core/Makefile文件中,如下所示:

[plain] view plain copy



        INSTALLED_USERDATAIMAGE_TARGET的值等于BUILT_USERDATAIMAGE_TARGET,后者指向的就是userdata.img文件,它依赖于INTERNAL_USERDATAIMAGE_FILES描述的文件,即那些由ALL_DEFAULT_INSTALLED_MODULES描述的、并且位于TARGET_OUT_DATA目录下的文件。假设我们的编译目标产品是模拟器,那么TARGET_OUT_DATA对应的目录就为out/target/product/generic/data。此外,如果我们在make userdataimage的时候,还带有一个额外的tests目标,那么那些将自己的tag设置为tests的模块也会被打包到userdata.img镜像中。

        INSTALLED_USERDATAIMAGE_TARGET还依赖于INTERNAL_USERIMAGES_DEPS。前面在分析system.img镜像的生成过程时提到,INTERNAL_USERIMAGES_DEPS描述的是制作userdata.img镜像所依赖的工具。例如,如果要制作的userdata.img使用的是yaffs2文件系统,那么对应工具就是mkyaffs2image。

        INSTALLED_USERDATAIMAGE_TARGET规则由函数build-userdataimage-target来执行,该函数通过build_image.py脚本来生成userdata.img镜像文件。

        与system.img镜像类似,userdata.img镜像的生成也可以通过一个没有任何依赖的伪目标userdataimage-nodeps生成,如下所示:


[plain] view plain copy



        当我们执行make userdataimage-nodeps的时候,函数build-userdataimage-target就会被调用直接生成userdata.img文件。

        五. recovery.img

        recovery.img是设备进入recovery模式时所加载的镜像。recovery模式就是用来更新系统的,我们可以认为我们的设备具有两个系统。一个是正常的系统,它由boot.img、system.img、ramdisk.img和userdata.img等组成,另外一个就是recovery系统,由recovery.img组成。平时我们进入的都是正常的系统,只有当我们需要更新这个正常的系统时,才会进入到recovery系统。因此,我们可以将recovery系统理解为在Linux Kernel之上运行的一个小小的用户空间运行时。这个用户空间运行时可以访问我们平时经常使用的那个系统的文件,从而实现对它的更新。

       在build/core/Makefile文件中,定义了一个伪目标recoveryimage,用来生成recovery.img,如下所示:


[plain] view plain copy



       INSTALLED_RECOVERYIMAGE_TARGET描述的是组成recovery系统的模块文件,而RECOVERY_RESOURCE_ZIP描述的是recovery系统使用的资源包,它的定义如下所示:

[plain] view plain copy



        由于recovery.img和boot.img都是用来启动系统的,因此,它们的内容是比较类似,例如都包含有Kernel及其启动参数、Ramdisk,以及可选的BootLoader第二阶段。此外,recovery.img还包含有以下的主要内容:

        1. 用来设置系统属性的文件build.prop和default.prop;

        2. 进入recovery界面时用到的资源文件recovery_resources_common和recovery_resources_private;

        3. 描述进入recovery模式时要安装的文件系统脚本recovery.fstab;

        4. 在recovery模式执行OTA更新要用到的ZSOTA_PUBLIC_KEYS;

        5. 负责升级系统的recovery程序,它的源代码位于bootable/recovery目录中。

        通过对比Android正常使用时的系统和进行升级时使用的Recovery系统,我们就可以对运行Linux系统的移动设备(或者说嵌入式设备)世界窥一斑而知全豹,实际上我们只需要有一个BootLoader、一个Linux Kernel,以及一个Ramdisk,就可以将它们运行起来。Ramdisk属于用户空间的范畴,它里面主要包含了Linux Kernel启动完成时所要加载的根文件系统。这个根文件系统的作用就是为整个系统提供一个init程序,以便Linux Kernel可以将控制权从内核空间转移到用户空间。系统提供什么样的功能给用户使用主要取决于在用户空间层运行了哪些服务和守护进程。这些服务守护进程都是直接或者间接地由init进程启动的。例如,对于recovery系统来说,它最主要提供的功能就是让用户升级系统,也就是它的主要用户空间服务就是负责执行长级任务的recovery程序。又如,我们正常使用的Android系统,它的主要用户空间服务和守护进程都是由system.img镜像提供的,此外,我们还可以通过userdata.img镜像来安装第三方应用来丰富手机的功能。因此,我们也可以说,init进程启动的服务决定了系统的功能。当然 ,这些功能是建立是硬件的基础之上的。

        至此,我们就分析完成Android系统的主要镜像文件system.img、boot.img、ramdisk.img、userdata.img和recovery.img的制作过程了,希望通过这些镜像文件的制作过程以及它们的作用的介绍,使得小伙伴后对Android系统有更进一步的认识。同时,我们也结束了对Android编译系统的学习了。



作者: liuwei    时间: 2016-9-28 14:10





欢迎光临 SuperIC社区_ (/) Powered by Discuz! X3.3