Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android編譯系統分析(五)-system.img的生成過程

Android編譯系統分析(五)-system.img的生成過程

編輯:關於Android編程

我們在完整編譯android系統的時候,最終會生成幾個重要的鏡像文件,其中有system.img,userdata.img,ramdisk.img等。這篇文章的目的是分析system.img的生成過程。
回想下我們完整編譯android系統時的動作,我們會在android源碼頂級目錄執行make命令,這樣就會完整的編譯android系統,我們沒有傳入任何參數(-jx等加快編譯的除外),因為我們沒有明確指定make的目標,所以android編譯系統會執行默認的編譯目標,也就是droid。因此,我們還是從droid著手,看看system.img怎麼生成。
我們只關注system.img相關的部分,其他部分都忽略,因此會有如下依賴關系:
system.img生成依賴圖

一.systemimage

# Rules that need to be present for the all targets, even
# if they don't do anything.
.PHONY: systemimage
systemimage:

sytemimage是一個偽目標,它並不會被生成。

systemimage: $(INSTALLED_SYSTEMIMAGE)

systemimage依賴於$(INSTALLED_SYSTEMIMAGE)

二.$(INSTALLED_SYSTEMIMAGE)

INSTALLED_SYSTEMIMAGE := $(PRODUCT_OUT)/system.img

INSTALLED_SYSTEMIMAGE變量的值就是system.img了,也就是說它就是我們最終要生成的目標。那麼看看它的定義:

$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)
    @echo "Install system fs image: $@"
    $(copy-file-to-target)
    $(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))

(INSTALLEDSYSTEMIMAGE)有依賴了(BUILT_SYSTEMIMAGE) 和(RECOVERYFROMBOOTPATCH)以及(ACP),我們目前無法知道這三個變量是什麼,當然,這裡的(ACP)是一種read?only依賴,也就是說(ACP)發生改變時,編譯器並不會重新生成system.img,(ACP)其實代表的是acp可執行文件,這個執行文件由acp.c文件生成,代碼在build/tools/acp/目錄下。因此,acp是一個生成system.img過程中使用的工具,它的改變不會使system.img立刻重新生成。雖然我們暫且不知道(BUILT_SYSTEMIMAGE) 和$(RECOVERY_FROM_BOOT_PATCH)代表的是什麼,但是我們可以先看看system.img的生成規則,看看生成規則是怎麼使用這三個依賴來生成system.img鏡像文件的的。

2.1$(copy-file-to-target)

copy-file-to-target的定義如下:

# Copy a single file from one place to another,
# preserving permissions and overwriting any existing
# file.
# We disable the "-t" option for acp cannot handle
# high resolution timestamp correctly on file systems like ext4.
# Therefore copy-file-to-target is the same as copy-file-to-new-target.
define copy-file-to-target
@mkdir -p $(dir $@)
$(hide) $(ACP) -fp $< $@
endef

結合注釋,這段代碼的功能是拷貝文件,並且在拷貝的過程中會保留文件的權限和覆蓋已有的文件。<代表的是第一個依賴,也就是這裡的(BUILT_SYSTEMIMAGE),這裡首先會創建/out/target/product/xxx/目錄,其中xxx是產品名,然後把(BUILTSYSTEMIMAGE)拷貝到該目錄下並命名為system.img。因此,system.img誕生。所以說它的誕生是由(BUILT_SYSTEMIMAGE)變量所代表的文件直接拷貝而來,因此,要搞清system.img的生成過程,必須搞清$(BUILT_SYSTEMIMAGE)的生成過程。

2.2assert-max-image-size

緊隨其後的assert-max-image-size函數又做了什麼呢?調用它的時候傳入了兩個參數,分別是1.system.img 2.(RECOVERYFROMBOOTPATCH),(BOARD_SYSTEMIMAGE_PARTITION_SIZE)
(RECOVERYFROMBOOTPATCH)是一個補丁文件:RECOVERYFROMBOOTPATCH:=(intermediates)/recovery_from_boot.p
$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)則是一個數字,不同的產品這個數字不同:
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1610612736
assert-max-image-size的定義如下:

# Like assert-max-file-size, but the second argument is a partition
# size, which we'll convert to a max image size before checking it
# against the files.
#
# $(1): The file(s) to check (often $@)
# $(2): The partition size.
define assert-max-image-size
$(if $(2), \
  $(call assert-max-file-size,$(1),$(call image-size-from-data-size,$(2))))
endef

image-size-from-data-size函數如下:

# Convert a partition data size (eg, as reported in /proc/mtd) to the
# size of the image used to flash that partition (which includes a
# spare area for each page).
# $(1): the partition data size
define image-size-from-data-size
$(strip $(eval _isfds_value := $$(shell echo $$$$(($(1) / $(BOARD_NAND_PAGE_SIZE) * \
  ($(BOARD_NAND_PAGE_SIZE)+$(BOARD_NAND_SPARE_SIZE))))))\
$(if $(filter 0, $(_isfds_value)),$(shell echo $$(($(BOARD_NAND_PAGE_SIZE)+$(BOARD_NAND_SPARE_SIZE)))),$(_isfds_value))\
$(eval _isfds_value :=))
endef

可以看到這個函數對分區大小做一個轉換,轉換為flash芯片上的分區大小。之後把轉換後的結果傳給assert-max-file-size作為第二個參數。
assert-max-file-size定義如下:

# $(1): The file(s) to check (often $@)
# $(2): The maximum total image size, in decimal bytes.
#    Make sure to take into account any reserved space needed for the FS.
#
# If $(2) is empty, evaluates to "true"
#
# Reserve bad blocks.  Make sure that MAX(1% of partition size, 2 blocks)
# is left over after the image has been flashed.  Round the 1% up to the
# next whole flash block size.
define assert-max-file-size
$(if $(2), \
  size=$$(for i in $(1); do $(call get-file-size,$$i); echo +; done; echo 0); \
  total=$$(( $$( echo "$$size" ) )); \
  printname=$$(echo -n "$(1)" | tr " " +); \
  img_blocksize=$(call image-size-from-data-size,$(BOARD_FLASH_BLOCK_SIZE)); \
  twoblocks=$$((img_blocksize * 2)); \
  onepct=$$((((($(2) / 100) - 1) / img_blocksize + 1) * img_blocksize)); \
  reserve=$$((twoblocks > onepct ? twoblocks : onepct)); \
  maxsize=$$(($(2) - reserve)); \
  echo "$$printname maxsize=$$maxsize blocksize=$$img_blocksize total=$$total reserve=$$reserve"; \
  if [ "$$total" -gt "$$maxsize" ]; then \
    echo "error: $$printname too large ($$total > [$(2) - $$reserve])"; \
    false; \
  elif [ "$$total" -gt $$((maxsize - 32768)) ]; then \
    echo "WARNING: $$printname approaching size limit ($$total now; limit $$maxsize)"; \
  fi \
 , \
  true \
 )
endef

這個函數對system.img的大小做一個檢查,如果system.img太大,超過了flash允許的最大分區的大小,這裡就會報錯。
因此,assert-max-image-size函數可以理解為檢查system.img的合法性。

三.$(BUILT_SYSTEMIMAGE)

我們分析system.img的生成規則發現,system.img其實是(BUILTSYSTEMIMAGE)的一份拷貝。那麼(BUILT_SYSTEMIMAGE)又是怎麼生成的呢?
(BUILTSYSTEMIMAGE)其實也是一個sytem.img文件,只不過它在(systemimage_intermediates)目錄下:
BUILT_SYSTEMIMAGE := (systemimageintermediates)/system.img(systemimage_intermediates) := target/product/xxx/obj/PACKAGING/systemimage_intermediates
$(BUILT_SYSTEMIMAGE)的依賴與生成規則如下:

$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
    $(call build-systemimage-target,$@)

我們不知道它依賴的是什麼,但是我們可以先看一下它的生成規則:
build-systemimage-target函數定義如下:

# $(1): output file
define build-systemimage-target
  @echo "Target system fs image: $(1)"
  $(call create-system-vendor-symlink)
  @mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt
  $(call generate-userimage-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt, \
      skip_fsck=true)
  $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
      ./build/tools/releasetools/build_image.py \
      $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \
      || ( echo "Out of space? the tree size of $(TARGET_OUT) is (MB): " 1>&2 ;\
           du -sm $(TARGET_OUT) 1>&2;\
           if [ "$(INTERNAL_USERIMAGES_EXT_VARIANT)" == "ext4" ]; then \
               maxsize=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE); \
               if [ "$(BOARD_HAS_EXT4_RESERVED_BLOCKS)" == "true" ]; then \
                   maxsize=$$((maxsize - 4096 * 4096)); \
               fi; \
               echo "The max is $$(( maxsize / 1048576 )) MB." 1>&2 ;\
           else \
               echo "The max is $$(( $(BOARD_SYSTEMIMAGE_PARTITION_SIZE) / 1048576 )) MB." 1>&2 ;\
           fi; \
           mkdir -p $(DIST_DIR); cp $(INSTALLED_FILES_FILE) $(DIST_DIR)/installed-files-rescued.txt; \
           exit 1 )
endef

這個函數做了四件事情:

define create-system-vendor-symlink
$(hide) if [ -d $(TARGET_OUT)/vendor ] && [ ! -h $(TARGET_OUT)/vendor ]; then \
  echo 'Non-symlink $(TARGET_OUT)/vendor detected!' 1>&2; \
  echo 'You cannot install files to $(TARGET_OUT)/vendor while building a separate vendor.img!' 1>&2; \
  exit 1; \
fi
$(hide) ln -sf /vendor $(TARGET_OUT)/vendor
endef

如果存在vendor目錄,就給vendor目錄創建一個軟連接。

2.創建target/product/xxx/obj/PACKAGING/systemimage_intermediates目錄並刪除這個目錄下的system_image_info.txt文件。

3.重新向system_image.info.txt中寫入數據

# $(1): the path of the output dictionary file
# $(2): additional "key=value" pairs to append to the dictionary file.
define generate-userimage-prop-dictionary
$(if $(INTERNAL_USERIMAGES_EXT_VARIANT),$(hide) echo "fs_type=$(INTERNAL_USERIMAGES_EXT_VARIANT)" >> $(1))
$(if $(BOARD_SYSTEMIMAGE_PARTITION_SIZE),$(hide) echo "system_size=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "system_fs_type=$(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
$(if $(BOARD_SYSTEMIMAGE_JOURNAL_SIZE),$(hide) echo "system_journal_size=$(BOARD_SYSTEMIMAGE_JOURNAL_SIZE)" >> $(1))
$(if $(BOARD_HAS_EXT4_RESERVED_BLOCKS),$(hide) echo "has_ext4_reserved_blocks=$(BOARD_HAS_EXT4_RESERVED_BLOCKS)" >> $(1))
$(if $(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "system_squashfs_compressor=$(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
$(if $(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "system_squashfs_compressor_opt=$(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
$(if $(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "userdata_fs_type=$(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
$(if $(BOARD_USERDATAIMAGE_PARTITION_SIZE),$(hide) echo "userdata_size=$(BOARD_USERDATAIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "cache_fs_type=$(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
$(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),$(hide) echo "cache_size=$(BOARD_CACHEIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "vendor_fs_type=$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
$(if $(BOARD_VENDORIMAGE_PARTITION_SIZE),$(hide) echo "vendor_size=$(BOARD_VENDORIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_VENDORIMAGE_JOURNAL_SIZE),$(hide) echo "vendor_journal_size=$(BOARD_VENDORIMAGE_JOURNAL_SIZE)" >> $(1))
$(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo "oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_OEMIMAGE_JOURNAL_SIZE),$(hide) echo "oem_journal_size=$(BOARD_OEMIMAGE_JOURNAL_SIZE)" >> $(1))
$(if $(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG),$(hide) echo "extfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG)" >> $(1))
$(hide) echo "selinux_fc=$(SELINUX_FC)" >> $(1)
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_BOOT_SIGNER),$(hide) echo "boot_signer=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_BOOT_SIGNER)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),$(hide) echo "verity=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),$(hide) echo "verity_key=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),$(hide) echo "verity_signer_cmd=$(VERITY_SIGNER)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_VERITY_PARTITION),$(hide) echo "system_verity_block_device=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_VERITY_PARTITION)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VENDOR_VERITY_PARTITION),$(hide) echo "vendor_verity_block_device=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VENDOR_VERITY_PARTITION)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot_key=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "futility=$(FUTILITY)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot_signer_cmd=$(VBOOT_SIGNER)" >> $(1))
$(if $(filter true,$(BOARD_BUILD_SYSTEM_ROOT_IMAGE)),\
    $(hide) echo "system_root_image=true" >> $(1);\
    echo "ramdisk_dir=$(TARGET_ROOT_OUT)" >> $(1))
$(if $(2),$(hide) $(foreach kv,$(2),echo "$(kv)" >> $(1);))
endef

4.使用build_image.py腳本生成system.img鏡像文件。

四.$(FULL_SYSTEMIMAGE_DEPS)

FULL_SYSTEMIMAGE_DEPS又有以下兩部分組成:
FULL_SYSTEMIMAGE_DEPS := (INTERNALSYSTEMIMAGEFILES)(INTERNAL_USERIMAGES_DEPS)

1.$(INTERNAL_SYSTEMIMAGE_FILES)

INTERNAL_SYSTEMIMAGE_FILES := $(filter $(TARGET_OUT)/%, \
    $(ALL_PREBUILT) \
    $(ALL_COPIED_HEADERS) \
    $(ALL_GENERATED_SOURCES) \
    $(ALL_DEFAULT_INSTALLED_MODULES) \
    $(PDK_FUSION_SYSIMG_FILES) \
    $(RECOVERY_RESOURCE_ZIP) \

從這裡就可以看出,INTERNAL_SYSTEMIMAGE_FILES描述的就是從ALL_PREBUILT、ALL_COPIED_HEADERS、ALL_GENERATED_SOURCES、ALL_DEFAULT_INSTALLED_MODULES、PDK_FUSION_SYSIMG_FILES和RECOVERY_RESOURCE_ZIP中過濾出來的存放在TARGET_OUT目錄下的那些文件,即在目標產品輸出目錄中的system子目錄下那些文件。
ALL_PREBUILT:要拷貝到目標設備上去的文件。
ALL_COPIED_HEADERS:要拷貝到目標設備上去的頭文件。
ALL_GENERATED_SOURCES:要拷貝到目標設備上去的由工具自動生成的源代碼文件。
ALL_DEFAULT_INSTALLED_MODULES:要安裝要目標設備上的所有的模塊文件。
PDK_FUSION_SYSIMG_FILES是從PDK(Platform Development Kit)提取出來的相關文件。
RECOVERY_RESOURCE_ZIP描述的是Android的recovery系統要使用的資源文件,對應於/system/etc目錄下的recovery-resource.dat文件。

2.$(INTERNAL_USERIMAGES_DEPS)

ifeq ($(INTERNAL_USERIMAGES_USE_EXT),true)
INTERNAL_USERIMAGES_DEPS := $(SIMG2IMG)
INTERNAL_USERIMAGES_DEPS += $(MKEXTUSERIMG) $(MAKE_EXT4FS) $(E2FSCK)
ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) $(MAKE_F2FS)
endif
endif

ifeq ($(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),squashfs)
INTERNAL_USERIMAGES_DEPS += $(MAKE_SQUASHFS) $(MKSQUASHFSUSERIMG) $(IMG2SIMG)
endif

ifeq ($(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),squashfs)
INTERNAL_USERIMAGES_DEPS += $(MAKE_SQUASHFS) $(MKSQUASHFSUSERIMG) $(IMG2SIMG)
endif

INTERNAL_USERIMAGES_BINARY_PATHS := $(sort $(dir $(INTERNAL_USERIMAGES_DEPS)))

ifeq (true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY))
INTERNAL_USERIMAGES_DEPS += $(BUILD_VERITY_TREE) $(APPEND2SIMG) $(VERITY_SIGNER)
endif

SELINUX_FC := $(TARGET_ROOT_OUT)/file_contexts
INTERNAL_USERIMAGES_DEPS += $(SELINUX_FC)

從以上可以看出INTERNAL_USERIMAGES_DEPS描述的是制作system.img鏡像所依賴的工具。例如,如果要制作的system.img使用的是yaffs2文件系統,那麼對應工具就是mkyaffs2image。
總結:也就是四小節提供鏡像打包工具和所有需要的文件,這些文件在之前的編譯中已經生成好了,然後交由三小節的build-systemimage-target函數使用build_image.py生成system.img鏡像文件,這個鏡像文件在target/product/xxx/obj/PACKAGING/systemimage_intermediates目錄下,之後再由二小節中的拷貝函數將其拷貝到target/product/xxx目錄下,xxx是產品名。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved