android 根文件系统,使用mkbootfs制作ramdisk根文件系统

环境

Qemu:QEMU emulator version 3.1.0

Linux:Linux-4.14.13

工具链:arm-none-linux-gnueabi-gcc  (gcc version 4.8.3 20140320)

Android:7.1.2

busyBox:BusyBox v1.24.2

概述

Android系统使用的根文件系统是用mkbootfs和minigzip制作的,其中mkbootfs用于将根文件系统打包成cpio格式,也可以用cpio工具来打包,将来Linux内核在启动时会调用init/initramfs.c中的函数unpack_to_rootfs对cpio格式进行解包[调用路径:start_kernel --> rest_init --> kernel_init --> kernel_init_freeable --> do_basic_setup --> do_initcalls --> do_initcall_level --> do_one_initcall --> populate_rootfs --> unpack_to_rootfs],在内存中构造出根文件系统结构,mkbootfs工具是Android自己实现的,支持的功能也比cpio弱很多。minigzip也是Android实现的一个压缩工具,是对gzip的简化。

minigzip的源码位于:external/zlib/

mkbootfs的源码位于:system/core/cpio/mkbootfs.c

不过需要注意mkbootfs的功能要比cpio弱,从mkbootfs.c的代码注释中可以看出来:

/*NOTES

**

** - see buffer-format.txt from the linux kernel docs for

** an explanation of this file format

** - dotfiles are ignored

** - directories named ‘root‘ are ignored

** - device notes,pipes,etc are not supported (error)*/

上面的信息说明了如下几点:

1. 对cpio格式的说明,Linux内核文档Documentation/early-userspace/buffer-format.txt中有详细说明说明:

The full format of the initramfs buffer is defined by the following

grammar,where:* is used to indicate "0 or more occurrences of"(|) indicates alternatives+indicates concatenation

GZIP() indicates thegzip(1) of the operand

ALGN(n) means padding withnull bytes to an n-byteboundary

initramfs := ("\0" | cpio_archive | cpio_gzip_archive)*cpio_gzip_archive :=GZIP(cpio_archive)

cpio_archive := cpio_file* + ( |cpio_trailer)

cpio_file := ALGN(4) + cpio_header + filename + "\0" + ALGN(4) +data

cpio_trailer := ALGN(4) + cpio_header + "TRAILER!!!\0" + ALGN(4)

2. 名为"."的文件会被忽略,不会进行打包。

3. 名为"root"的文件也会被忽略,不会进行打包。

4. 不支持设备节点以及管道文件。所以在使用mkbootfs时,需要确保被打包的路径下没有这两种文件,否者会导致错误,并且Linux内核也无法正常访问指定的文件。

此外,在使用mkbootfs时还需要注意的时,mkbootfs会对打包的文件的权限以及uid和gid进行修改(在函数fix_stat中),有可能会遇到本来具备可执行权限的文件,在Linux里解包后,可执行权限丢失了,比如/etc/init.d/rcS。

lib_path=`readlink -f ./lib`

bin_path=`readlink -f ./bin/`

export LD_LIBRARY_PATH=${lib_path}:$LD_LIBRARY_PATH

export PATH=${bin_path}:$PATHrm -f ramdisk.img ramdisk.cpiorm -rf ./tmp/# pack

pushd rootfs2

mkbootfs-f ../config.txt . | minigzip > ../ramdisk.img

mkbootfs-f ../config.txt . > ../ramdisk.cpio

popd

# unpackmkdir -p tmp

pushd tmp

cpio-i < ../ramdisk.cpio

popd

其中config.txt是一个配置文件,内容如下:

etc/init.d/rcS 0 0 0755

0 0 0744

第1行,把"etc/init.d/rcS"文件的uid设置为0,gid设置为0,权限设置为0755

第2行,所有其他文件的uid都设置0,gid也设置为0,权限设置为0744

需要注意的是,第2行开始有一个空格,并且需要放在最后一行,对具体文件的设置要放到前面。详见mkbootfs.c中函数read_canned_config。

如果没有个指定config.txt,那么会使用Android代码中自带的配置机制[在fix_stat中会调用fs_config],功能要比config.txt更完善,如果在使用mkbootfs时还设置了-d ,那么函数fs_config会优先使用/system/etc/fs_config_dirs和/system/etc/fs_config_files中描述的规则,前者针对目录,后者针对其他文件。如果没有给mkbootfs传递-d 参数,那么使用Android在源码中指定的规则,它们存放在两个数组中,一个针对目录,一个针对其他文件:

针对目录的配置规则:

/*Rules for directories.

** These rules are applied based on "first match",so they

** should start with the most specific path and work their

** way up to the root.*/

static const struct fs_path_config android_dirs[] ={

{00770,AID_SYSTEM,AID_CACHE,0,"cache"},{00500,AID_ROOT,"config"},{00771,"data/app"},"data/app-private"},"data/app-ephemeral"},"data/dalvik-cache"},"data/data"},AID_SHELL,"data/local/tmp"},"data/local"},{01771,AID_MISC,"data/misc"},{00770,AID_DHCP,"data/misc/dhcp"},AID_SHARED_RELRO,"data/misc/shared_relro"},{00775,AID_MEDIA_RW,"data/media"},"data/media/Music"},{00750,"data/nativetest"},"data/nativetest64"},... ...

针对其他文件的规则:

static const struct fs_path_config android_files[] ={

{00440,"system/etc/init.goldfish.rc"},{00550,"system/etc/init.goldfish.sh"},"system/etc/init.ril"},{00555,"system/etc/ppp/*"},"system/etc/rc.*"},{00440,"system/etc/recovery.img"},{00444,conf_dir + 1},conf_file + 1},{00644,"data/app/*"},"data/media/*"},"data/app-private/*"},"data/app-ephemeral/*"},AID_APP,"data/data/*"},{00640,"data/nativetest/tests.txt"},"data/nativetest64/tests.txt"},... ...

验证

1.  使用压缩格式的ramdisk.img (mkbootfs -f ../config.txt . | minigzip > ../ramdisk.img)

kernel_dir=./Linux-4.14.13kernel_image=${kernel_dir}/arch/arm/boot/zImage

dtb_image=${kernel_dir}/arch/arm/boot/dts/vexpress-v2p-ca9.dtb

qemu_path=/home/pengdonglin/disk_ext/Qemu/qemu-3.1.0/build/install/bin

${qemu_path}/qemu-system-arm-M vexpress-a9-m 1024M-smp 1-kernel ${kernel_image}-nographic-append "root=/dev/ram0 rw rootfstype=ramfs console=ttyAMA0 init=/linuxrc ignore_loglevel"-initrd ./rootfs/ramdisk.img-dtb ${dtb_image}

部分启动log:

[ 0.609270] Trying to unpack rootfs image as initramfs...

[0.965940] Freeing initrd memory: 3616K

2. 使用非压缩的ramdisk.cpio (mkbootfs -f ../config.txt . > ../ramdisk.cpio)

kernel_dir=./Linux-4.14.13kernel_image=${kernel_dir}/arch/arm/boot/zImage

dtb_image=${kernel_dir}/arch/arm/boot/dts/vexpress-v2p-ca9.dtb

qemu_path=/home/pengdonglin/disk_ext/Qemu/qemu-3.1.0/build/install/bin

${qemu_path}/qemu-system-arm-M vexpress-a9-m 1024M-smp 1-kernel ${kernel_image}-nographic-append "root=/dev/ram0 rw rootfstype=ramfs console=ttyAMA0 init=/linuxrc ignore_loglevel"-initrd ./rootfs/ramdisk.cpio-dtb ${dtb_image}

部分启动log:

[ 0.610055] Trying to unpack rootfs image as initramfs...

[0.760468] Freeing initrd memory: 7040K

此外,Linux内核本身也支持在编译时将指定的外部根文件系统编译成cpio.gz格式,然后跟Linux内核链接到一起,在启动时就不需要指定initrd了,Linux内核支持的打包工具usr/gen_init_cpio.c也比mkbootfs强大:

配置内核:

General setup --->

[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support

(/home/pengdonglin/aarch32/rootfs/rootfs) Initramfs source file(s)

Linux内核对这部分的处理请参考usr/Makefile,下面是加入上面的配置之后的部分内核编译log:

GEN usr/initramfs_data.cpio.gz

GZIP kernel/config_data.gz

CHK kernel/config_data.h

UPD kernel/config_data.h

CC kernel/configs.o

AR kernel/built-in.o

AS usr/initramfs_data.o

AR usr/built-in.o

展开:

/bin/bash ./scripts/gen_initramfs_list.sh -o usr/initramfs_data.cpio.gz -u 0 -g 0 /home/pengdonglin/disk_ext/Qemu/aarch32/rootfs/rootfs

下面是测试命令:

kernel_dir=./Linux-4.14.13kernel_image=${kernel_dir}/arch/arm/boot/zImage

dtb_image=${kernel_dir}/arch/arm/boot/dts/vexpress-v2p-ca9.dtb

qemu_path=/home/pengdonglin/disk_ext/Qemu/qemu-3.1.0/build/install/bin

${qemu_path}/qemu-system-arm-M vexpress-a9-m 1024M-smp 1-kernel ${kernel_image}-nographic-append "root=/dev/ram0 rw rootfstype=ramfs console=ttyAMA0 init=/linuxrc ignore_loglevel"-dtb ${dtb_image}

此外,在Documentation/filesystems/ramfs-rootfs-initramfs.txt提供了一个使用cpio打包的脚本:

185 #!/bin/sh

186

187 # Copyright 2006 Rob Landley and TimeSys Corporation.188 # Licensed under GPL version 2

189

190 if [ $# -ne 2]191 then

192 echo "usage: mkinitramfs directory imagename.cpio.gz"

193 exit 1

194 fi

195

196 if [ -d "$1"]197 then

198 echo "creating $2 from $1"

199 (cd "$1"; find . | cpio -o -H newc | gzip) > "$2"

200 else

201 echo "First argument must be a directory"

202 exit 1

203 fi

完。