Android构建系统 - 04 编译产物

文章目录

编译产物

目录结构

经过make编译后的产物,都位于/out目录,该目录下主要关注下面几个目录:

  • /out/host

    • Android开发工具的产物,包含SDK各种工具,比如adb,dex2oat,aapt等。
  • /out/target/common

    • 通用的一些编译产物,包含Java应用代码和Java库;
  • /out/target/product/[product_name]

    • 针对特定设备的编译产物以及平台相关C/C++代码和二进制文件;

    • 几个重量级的镜像文件:

      • system.img:挂载为根分区,主要包含Android OS的系统文件;
      • ramdisk.img:主要包含init.rc文件和配置文件等;
      • userdata.img:被挂载在/data,主要包含用户以及应用程序相关的数据;
      • 其它镜像文件:boot.img,reocovery.img等,这里就不介绍了

镜像文件 .img

Android编译后生成文件,在out/target/product/xxx下:

system.img

挂载为根分区,主要包含Android OS系统的核心文件;

可以添加:

  • Android系统应用
  • 更多的library
/system目录

系统镜像是地址ROM最常使用的一个镜像,

  • 设备中system目录的镜像。
  • 用于存储Android系统的核心文件,主要的目录和文件

system.img.raw挂载出来后,该目录中的内容实际上与system.img中的内容完全一样,现在可以任意修改目录中的内容,再进行打包以达到更新system.img的目的。

文件列表如下:

目录 意义
app 存放一般的apk文件。
bin 存放一些Linux的工具,但是大部分都是toolbox的链接.
etc 存放系统的配置文件。
fonts 存放系统的字体文件。
framework 存放系统平台所有jar包和资源文件包。
lib 存放系统的共享库。
media 存放系统的多媒体资源,主要是铃声。
priv-app android4.4开始新增加的目录,存放系统核心的apk文件。
tts 存放系统的语言合成文件。
usr 存放各种键盘布局,时间区域文件。
vendor 存放一些第三方厂商的配置文件、firmware以及动态库。
xbin 存放系统管理工具,这个文件夹的作用相当于标准Linux文件系统中的sbin.
build.prop文件 系统属性的定义文件。
压缩 解压
解压

为了搞清楚system.img镜像中的内容,可以将其解压:

旧版

旧版的镜像

  • yaffs格式的(通过mkyaffs2image工具制作的)
  • 可以使用unyafss命令对其解压。

Android源代码中并未提供该命令,所以读者可以到 http://code.google.com/p/unyaffs/downloads/list 下载unyaffs的二进制程序和源代码。

bash 复制代码
unyaffs system.img

如果对编译Android源代码生成的system.img文件执行上面的命令,可以完美的将system.img文件还原成system目录,会从system目录中看到相应的子目录,例如,/system/app、/system/lib等,实际上,system.img文件就是out/target/product/generic/system中的文件压缩生成的。

高版本Android的system.img

  • 通常是ext4格式的文件系统镜像(通过make_ext4工具制作),

    由于system.img是压缩格式,所以并不能直接使用mount命令挂载。

  • 可以使用simg2img工具进行转换后挂载。

    在编译Android 源代码后会在Android源代码目录/out/host/linux-x86/bin目录生成一个simg2img命令行工具

    建议将该目录加到PATH环境变量中,因为当中的各种命令行工具会被经常使用。

simg2img可以通过如下的命令将system.img转化为普通的Linux镜像文件(system.img.raw);

bash 复制代码
# 转换
$ simg2img system.img system.img.raw

# 查看 镜像格式
$ file system.img
system.img: Android sparse image, version: 1.0, Total of 229673 4096-byte output blocks in 22 input chunks.

# 查看 镜像格式
$ file system.img.raw
system.img.raw: Linux rev 1.0 ext2 filesystem data, UUID=efee3fdf-d4f1-5e88-9f69-57632c5d8db4 (extents) (large files) (huge files)
挂载

此后,我们就可以进行挂载了:

bash 复制代码
## 挂载到 ~/debug/system
mkdir /mnt/system -p
sudo mount system.img.raw /mnt/system
压缩

在修改完系统镜像后,使用make_extfs命令将挂载目录重新生成system.img文件(EXT4文件系统)。

可以在Linux终端执行如下的命令生成system.img文件。

bash 复制代码
make_ext4fs -s -l 1024M -a system system.img /mnt/system

在执行make_ext4fs 命令使用了3个命令行参数,这些参数的含义如下:

  • -s:生成Spare格式的镜像。这种格式的镜像文件的尺寸会更小,但无法直接使用mount命令挂载。要想挂载Spare格式的镜像文件,需要首先使用simg2img命令按着前面描述的方式进行转换。如果不加-s参数,生成的system.img文件是可以直接通过mount挂载。不过不管是不是Spare格式的系统镜像文件,Nexus 7都可以使用(其他的Android设备应该也可以),但建议生成Spare格式的镜像文件,因为这样的镜像文件尺寸更小。
  • -l : 镜像的尺寸。该参数指定的值并不是生成镜像文件(r如system.img)的实际尺寸,而是文件系统的尺寸。这有些类似在Windows中建立的心得逻辑分区,而该参数指定的值就是逻辑分区的尺寸,生成的镜像文件的尺寸不能大于文件系统的尺寸。例如官方提供的用于Nexus 7的system.img文件(Spare格式的镜像文件)的尺寸大小越是480M,
  • -a: 指定挂载点,这里是system.
boot.img
组成

Linux内核镜像(boot.img)

boot.img中需要同时包含Linux内核(zImage)和ramdisk.img

  • 内核二进制(zImage

  • 内存磁盘镜像(ramdisk.img

    ramdisk.img中包含的init命令是与Linux内核第一个交互的程序

  • 配置文件:通常并不需要管这些文件,只需要保持默认值即可

    boot.img解压后,除了生成boot.img-zImageboot.img-ramdisk.gz文件外,还会生成一些其他的文件,如boot.img-baseboot.img-cmdlineboot.img-pagesize等,这些文件都是一些配置文件。

    例如boot.img-cmdline文件中包含了Linux内核启动时传入的参数。

    例如boot.img-cmdline文件中包含了Linux内核启动时传入的参数。通常并不需要管这些文件,只需要保持默认值即可)。

解压/压缩

Android源代码经过编译后,也可以在其中找到对boot.img解压和生成boot.img文件的命令。

  • unpackbooting为解压命令
  • mkbooting命令可以将zImageramdisk.img文件合并成boot.img

下面先来用unpackbooting命令将boot.img解压,再看看boot.img是不是有zImageramdisk.img文件组成的。

假设boot.img文件(我们可以使用从其他Rom压缩包中获得的boot.img,也可以使用通过Android源代码生成的boot.img)在当前目录中,使用下面的命令可以将boot.img文件解压到boot目录中。

bash 复制代码
mkdir boot
cd boot 
unpackbootimg -i  ../boot.img

执行完上面的命令后,会发现boot目录中多了几个文件,其中有两个主要的文件:boot.img-zImageboot.img-ramdisk.gz

前者是Linux 内核文件(与zImage文件完全一样),后者是内存磁盘镜像(与ramdisk.img完全一样)。为了证明boot.img-ramdisk.gzramdisk.img文件完全相等,可以使用下面的命令将boot.img-ramdisk.gz解压到ramdisk目录。

bash 复制代码
mkdir ramdisk
cd ramdisk
gunzip -c ../boot.img-ramdisk.gz | cpio  -i

目录结构与ramdisk.img 一样。

如果想向init.rc或其他文件中添加新的内容,或在内存磁盘镜像中添加新的命令,可以修改刚才由boot.img-ramdisk.gz 文件解压生成的ramdisk目录中的相应文件和目录的内容,然后使用下面的命令重新将ramdisk目录中的相应文件和目录的内容,然后使用下面的命令重新将ramdisk打包成boot.img-ramdisk.gz.new(当前目录是ramdisk)。

bash 复制代码
mkbootfs . | minigzip > ../boot.img-ramdisk.gz.new

接下来回到上一层目录,然后使用下面的命令将boot.img-zImageboot.img-ramdisk.gz.new文件合并成boot.img.new文件(为了区分boot.img,这里生成了boot.img.new文件)。

bash 复制代码
mkbootimg --kernel boot.img-zImage --ramdisk boot.img-ramdisk.gz.new -o boot.img.new

如果想修改Linux内核,需要下载Linux内核源代码(官方和CM都提供了相应Android设备的Linux内核源代码)

接下来退到上一层目录,然后使用下面的命令将boot.img-zImage 和boot.img-ramdisk.gz.new

现在可以使用下面的命令重新刷Linux内核(加上Android系统处于正常启动状态,并通过USB线和PC相连)。不过要注意的是Linux内核必须与当前Android设备匹配,否则刷完后Android设备有可能起不来。刷Linux内核不会对系统(system.img)和用户数据(userdata.img)造成任何影响。

bash 复制代码
adb reboot bootloader

fastboot flash boot boot.img,new 

fastboot reboot

重启Android设备后,如果我们修改了Linux内核和内存磁盘镜像,就会立刻生效。

注意:boot.img解压后,除了生成boot.img-zImageboot.img-ramdisk.gz文件外,还会生成一些其他的文件,如boot.img-baseboot.img-cmdlineboot.img-pagesize等,这些文件都是一些配置文件。

例如boot.img-cmdline文件中包含了Linux内核启动时传入的参数。通常并不需要管这些文件,只需要保持默认值即可)。

ramdisk.img

内存磁盘镜像 ramdisk.img 存储了Linux内核启动时要装载的核心文件。

ramdisk.img需要放在Linux内核镜像(boot.img)中,但却属于Android源代码的一部分。

init命令
  • ramdisk.img中包含的init命令是与Linux内核第一个交互的程序

    在root目录中可以找到该命名文件

  • 该命令用于读取init.rc以及相关配置文件中的初始化命令。

  • 所以在boot.img中需要同时包含Linux内核(zImage)和ramdisk.img

mount

根据解压出的文件 挂载 根文件系统

当Linux内核调用init后

android启动时 首先加载ramdisk.img镜像,并挂载到/目录下,并进行了一系列的初始化动作,包括创建各种需要的目录,初始化console,开启服务等,

  • 系统会根据init.rc及其相关文件的代码对整个Android系统进行初始化。

    • 建立如/system/data等系统目录,

    • 使用mount命令将相应的镜像挂载到这些目录上。

boot.img

ramdisk.imgboot.img中重要的组成部分之一

  • ramdisk.img也属于Android源代码的一部分。

    也就是说,在编译Android 源代码后,会生成一个ramdisk.img文件,

    其实该文件就是root目录压缩后生成的文件。

  • ramdisk.img需要放在Linux内核镜像(boot.img)中

映射在内存

映射的文件在内存中

  • ramdisk.img中的文件映射的实际上都是内存中的文件。

    • 对于这些文件,例如init.rc
    • 即使有权修改,也只是修改原始文件在内存中的镜像
    • 重新启动Android设备后,又会恢复到最初的状态
  • 修改这些文件的唯一方法

    • 生成boot.img文件
      • 重新制作ramdisk.img文件,
      • 连同Linux内核二进制文件(zImage)重新生成boot.img文件,
    • boot.img文件刷入设备中
解压

假设ramdisk.img文件在当前目录下,则还原ramdisk.img文件的命令如下:

bash 复制代码
mkdir ramdisk
cp ramdisk

gunzip -c ../ramdisk.img > ../ramdisk.cpio
cpio -i < ../ramdisk.cpio

可以将最后两行命令合成如下的一行。

bash 复制代码
gunzip -c ../ramdisk.img | cpio -i

执行上面的命令后,就会在ramdisk目录中看到内存磁盘镜像还原后的目录结构,如果现在要修改init.rc等配置文件,可以自己在ramdisk目录中找到相应的文件并修改。例如,有Linux的瑞士军刀之称busybox,可以放到ramdisk中的sbin目录下。这样在Recovery模式下就可以使用busybox命令完成很多操作了。

修改完ramdisk目录的内容后,就需要使用下面的命令将ramdisk目录重新生成ramdisk.img文件。

为了与原来的ramdisk.img文件有所区别,这里生成了ramdisk.img.new文件,在执行下面的命令之前,要保证Linux终端的当前目录是ramdisk。

bash 复制代码
cd XX # ramdisk目录
mkbootfs . | minigzip > ../ramdisk.img.new
vendor.img

包含所有不可分发给 Android 开源项目 (AOSP) 的二进制文件。如果没有专有信息,则可以省略此分区。

vbmeta.img

用于安全验证,bootloader验证vbmeta的签名,再用vbmeta的key以及hash值验证dtbo/boot/system/vendor。

  • vbmeta.img
  • vbmeta_system.img
userdata.img
/data目录

会被挂接到 /data 下,包含所有应用相关的配置文件,存储与用户相关的数据 。

  • 普通的应用程序,userdata.img

    这些数据大多都是有用户在使用Android设备的过程中产生的,

    • 通过Google play安装的第三方APK程序,用户的配置文件等。

    • 在制作ROM时,也可以将部分数据放到userdata.img中。

      例如,如果允许用户使用普通的方法卸载ROM内置的应用,就可以将APK文件放到userdata.img文件中 (

  • 系统应用程序,system.img

功能

userdata.img有如下两个功能:

  • 规定Android设备存储空间的大小
  • 封装与用户相关的文件(如果是APK程序,还允许卸载这些程序),
    • 连同ROM一起发布,
    • 或单独刷userdata.img文件。
存储区域

Android系统通过Linux 文件系统将可用的存储空间划分成不同区域,

  • userdata.img属于userdata分区,该分区就是存储空间。
  • 剩余的内存空间就会将其作为 外部存储器(/sdcard,目录)

在Android设备中可供用户操作的存储区域通常有如下:

RAM
  • RAM就是传统意义上的内存,与PC的内存是一个概念,
  • 只有在通电时才能存储数据,断点后所有数据将自动消失,所有要运行的程序都需要调用RAM。
存储空间

userdata.img就属于userdata分区,该分区就是前面所说的存储空间。

  • 现在所有的Android设备都有都带有一定大小的内部存储器

    (嵌入到芯片上,类似于内部嵌入一个SD卡),

  • 用于存储一些随机器发布的系统和应用程序。PC除了RAM,就是硬盘了。

外部存储器

外部存储器(通常是SD卡):

/sdcard,目录

  • 有的Android系统加入了OTG(On-The-Go)支持,
    • 通过OTG连接的U盘、移动硬盘也应属于外部存储器,
  • 有些Android设备(如Nexus 7) 不支持插入SD卡。
    • 如果不支持外部存储器,userdata.img就不能太大,
    • 否则系统将无法利用剩余的空间映射SD卡(/sdcard,目录)。
压缩解压
解压
  • 使用simg2img命令将userdata.img文件还原成非压缩格式的镜像文件,

  • 使用mount命令将userdata.img文件挂载到某个目录,

  • 进而查看userdata.img中的内容。

sh 复制代码
# 生成还原后的userdata.img.raw文件
simg2img userdata.img userdata.img.raw

# 挂载userdata.img.raw文件
mkdir -p /mnt/rom/userdata
mount  userdata.img.raw /mnt/rom/userdata

通常该目录除了"lost+found"(该目录一般为空,在系统非法关机时会存放一些文件) 系统目录外,什么都没有。

下面的命令,查看当前挂载的用户镜像尺寸。

bash 复制代码
df -h
压缩
  • 在打包的过程中会确定用户镜像对应的空间大小,

    下面的命令生成了最大为128M的用户镜像文件(userdata.img.new,ext4格式的镜像文件)

    sh 复制代码
    make_ext4fs -s -l 128M -a data userdata.img.new /mnt/rom/userdata
    • "-s"命令行,参数就表示生成的userdata.img.new 文件是压缩格式,不能直接使用mount命令挂载,需要按着签名的而方法通过simg2img命令来还原才能挂载到某一目录。
    • "-a"命令行参数后面的是文件系统,这里需要指定data。
  • 用户镜像占用的存储空间不能超过Android设备的内部存储器的总尺寸,

    • 否则即使成功刷机,Android设备也可能会启动失败,
    • 即使启动也由于SD卡无法挂载而出现要求输入密码(实际上就是映射失败)的情况。
烧录

在刷完userdata.img.new 后,重启Android设备。通过"设置"->"存储"可以查看使用情况。

sh 复制代码
adb reboot bootloader

fastboot flash userdata userdata.img.new

fastboot reboot
dtbo.img

设备树镜像(dtbo.img

现在的BootLoader(通常是uboot)一般与dtb文件相配合,以告知Linux有关驱动节点。

dtbo.img一般和boot.img相互作用,一般手机用户很难修改这一部分的内容。

recovery.img

recovery镜像 recovery.img

boot.img 相同 区别
相同

从本质上说,recovery.imgboot.img基本一样。

  • recovery.img也是Linux内核(zImage)和内存磁盘镜像(ramdisk.img)组成的。

    • 这两个镜像中的Linux内核是完全一样的,
    • 区别只是ramdisk.img中的少部分文件存在差异:
  • recovery.imgboot.img其实都是一个最小的运行系统,

    他们都各自带一个满足最低要求的运行环境(ramdisk.img)。

区别

功能上

  • recovery.imgboot.img其实都是一个最小的运行系统,

    他们都各自带一个满足最低要求的运行环境(ramdisk.img)。

    • boot.img利用这个运行环境监理更大的运行环境(system.img) ,

    • recovery.img就直接使用了这个运行环境进行基本的操作(复制文件、删除文件、加压文件、mount等),

      这些操作也就是Recovery模式下刷机要进行的一些操作。

文件内容

  1. recovery命令(最主要的差异)

recovery.img的ramdisk.img中的sbin目录中,

  • 多了一个recovery命令进入Recovery主界面,
  • 不会正常启动Android系统。
  1. init.rc及其相关配置文件

其中的init.rc及其相关配置文件的内容略有不同。

recovery.img和boot.img在自己的分区各自有一个Linux内核(zImage),彼此的Linux内核调用的init命令解析的init.rc及其相关文件的内容有一定的差异。

实现的原理

recovery.img和boot.img在自己的分区各自有一个Linux内核(zImage),彼此的Linux内核调用的init命令解析的init.rc及其相关文件的内容有一定的差异。

  • Bootloader根据用户的选择决定

    • 使用boot.img中Linux内核,去启动系统
      • Android系统就会正常启动,
    • 还是使用Recovery.img中的Linux内核,去启动系统。
      • 进入Recovery选择菜单,
  • recovery.img和boot.img其中的init.rc及其相关配置文件的内容略有不同

    • kernel加载结束以后第一个进程是执行init,

    • init会解析init.rc文件,并起相应的服务。

    • 由此可以知道正常开机和进入recovery模式起的进程是不同的。

  • boot.img利用这个运行环境监理更大的运行环境(system.img) ,

  • recovery.img就直接使用了这个运行环境进行基本的操作(复制文件、删除文件、加压文件、mount等),

    这些操作也就是Recovery模式下刷机要进行的一些操作。

压缩解压

既然了解了recovery.img是什么东西,那么就可以解压recovery.img,并且重写生成recovery.img文件。

假设recovery.img文件在当前目录下,解压recovery.img

bash 复制代码
mkdir recovery

cd recovery

uppackbootimg -i ../recovery.img

执行下面的命令会在recovery目录下生成如下5个文件:

bash 复制代码
recovery.img-zImage

recovery.img-ramdisk.gz

recovery.img-cmdline

recovery.img-pagesize

recovery.img-base

其中前两个分别为recovery.img中的Linux内核和内存磁盘镜像。可以使用下面的命令解压recovery.img-ramdisk.gz文件:

bash 复制代码
mkdir ramdisk

cd ramdisk

gunzip -c ../recovery.img-ramdisk.gz | cpio -i

现在回到上一层目录,最后按着4.2.4小节的方法重新生成内存镜像文件(这里为Recovery.img-randisk.gz.new),并使用下面的命令重新生成Recovery镜像(这里为recovery.img.new )。

重新生成Recovery镜像文件:

bash 复制代码
mkbootimg --kernel recovery.img-zImage --ramdisk recovery.img-ramdisk.gz.new -o recovery.img.new

现在可以使用下面的命令重新刷Recovery(加上Android 处在正常启动状态),并进入Recovery模式。

bash 复制代码
adb reboot bootloader

fastboot flash recovery recovery.img.new

fastboot reboot

adb reboot recovery
cache.img

缓存镜像(cache.img),用于存储系统或用户应用产生的临时数据。

一般ROM并不需要包含缓存镜像。

缓存镜像实际上就是一个空的ext4格式的文件系统镜像,可以使用下面的命令生成缓存镜像。

bash 复制代码
mkdir -p /mnt/rom/cache

make_ext4fs -s -l 256M -a cache cache.img /mnt/rom/cache

可以使用下面的abd命令刷缓存镜像:

bash 复制代码
adb reboot bootloader
fastboot flash cache cache.img
fastboot reboot
misc.img

misc 分区供恢复映像使用,存储空间不能小于 4KB。

其他

我们简单的来介绍out/target/product/generic/system目录下的常用目录:

shell 复制代码
# # Android系统自带的apk文件
out/target/product/generic/system/apk
# # 可执行文件(比如C编译的执行)
out/target/product/generic/system/bin
# # 动态链接库
out/target/product/generic/system/lib
# # 硬件抽象层文件
out/targer/product/generic/system/lib/hw

系统属性 build.prop

系统属性------/system/build.prop /vendor/build.prop

生成过程也是由编译系统控制的。

vendor

  • 通过在makefile 中追加 PRODUCT_PROPERTY_OVERRIDES 变量实现
  • 编译后在 out/target/product/pure/vendor/build.prop 文件
shell 复制代码
adb shell cat /system/build.prop
# 编译后在
out/target/product/pure/vendor/build.prop

system

  • 在BoardConfig.mk中,追加 TARGET_SYSTEM_PROP 变量

  • 写一份 system.prop

    $TARGET_DEVICE_DIR 目录创建一个 system.prop 文件,在里面添加属性即可。

shell 复制代码
adb shell cat /vendor/build.prop
# 编译后在
out/target/product/pure/system/build.prop

具体处理过程在/build/core/Makefile中。该Makefile由一下几个部分组成

  • /build/tools/buildinfo.sh
  • TARGET_DEVICE_DIR 目录下的 system.prop
  • ADDITIONAL_BUILD_PROPERTIES
  • /build/tools/post_process_props.py
相关推荐
我是唐青枫7 分钟前
Linux 下使用tracepath进行网络诊断分析
linux·运维·网络
s_little_monster19 分钟前
【Linux】进程间通信之匿名管道
linux·运维·服务器·数据库·经验分享·笔记·学习
dreamczf34 分钟前
基于Linux系统的物联网智能终端
linux·物联网·rtu·智能终端·水利
流情1 小时前
【Docker】Linux部署web版Firefox
linux·前端·docker
安於宿命1 小时前
【Linux】进程间通信——共享内存
linux·服务器·c++
小机学AI大模型1 小时前
Android Studio中gradle一栏中出现nothing to show 提示的解决方法
android·ide·android studio
九天轩辕1 小时前
RePlugin字节码修改流程分析
android·gradle
m0_748233642 小时前
RabbitMQ 进阶
android·前端·后端
likfishdn2 小时前
Linux的文件与目录管理
linux·运维·服务器
chian-ocean3 小时前
Linux 文件缓冲区:高效数据访问的幕后推手
linux·运维·服务器