转载:http://blog.chinaunix.net/uid-26009923-id-4068594.html
一. mkimage.sh
1. android目录下的mkimage.sh
out/target/..../kernel 多 kernel/kernel.img 首4b 52 4e 4c 24 d0 6c 00 尾:fb 90 e5 ca
################boot.img = (out/ramdisk.img + mkkrnling加入头尾标)
=>out/ramdisk.img = out/root压缩.
=>out/root =空文件夹(data,dev,proc,sys,system)+res(里面几张电源图片)+sbin(里面带adbd,e2fsck mkdosfs mke2fs resize2fs ueventd,watchdogd等二进制文件) +charger,init可执行文件+一些文件,具体如下:
-rwxr-xr-x 1 android android 264100 2014-07-22 18:01 charger*
drwxr-xr-x 2 android android 4096 2014-07-22 16:10 data/
-rw-r--r-- 1 android android 905 2014-07-22 16:13 default.prop
drwxr-xr-x 2 android android 4096 2014-07-22 16:10 dev/
-rwxr-xr-x 1 android android 7667 2014-07-22 17:05 drmboot.ko*
-rwxr-xr-x 1 android android 113644 2014-07-22 18:01 init*
-rw-r--r-- 1 android android 2487 2014-07-22 17:05 init.goldfish.rc
-rwxr-xr-x 1 android android 20012 2014-07-24 17:27 init.rc*
-rw-r--r-- 1 android android 394 2014-07-22 17:05 init.rk30board.bootmode.emmc.rc
-rw-r--r-- 1 android android 327 2014-07-22 17:05 init.rk30board.bootmode.unknown.rc
-rwxr-xr-x 1 android android 11770 2014-07-22 17:05 init.rk30board.rc*
-rwxr-xr-x 1 android android 6122 2014-07-22 17:05 init.rk30board.usb.rc*
-rw-r--r-- 1 android android 1795 2014-07-22 17:05 init.trace.rc
-rw-r--r-- 1 android android 2562 2014-07-22 17:05 init.usb.rc
drwxr-xr-x 2 android android 4096 2014-07-22 16:10 proc/
drwxr-xr-x 3 android android 4096 2014-07-22 17:06 res/
-rwxr-xr-x 1 android android 150405 2014-07-22 17:05 rk30xxnand_ko.ko.3.0.36+*
-rwxr-xr-x 1 android android 145284 2014-07-22 17:05 rk30xxnand_ko.ko.3.0.8+*
drwxr-xr-x 2 android android 4096 2014-07-22 18:01 sbin/
drwxr-xr-x 2 android android 4096 2014-07-22 16:10 sys/
drwxr-xr-x 2 android android 4096 2014-07-22 16:10 system/
-rw-r--r-- 1 android android 272 2014-07-22 17:05 ueventd.goldfish.rc
-rwxr-xr-x 1 android android 4547 2014-07-22 17:05 ueventd.rc*
-rwxr-xr-x 1 android android 3106 2014-07-22 17:05 ueventd.rk30board.rc*
总结:boot.img由 out/.../root/里面的文件通过minizip压缩后,在经过rkkrnlimg 加入头尾标志而生成的。
boot_ota.img则多一个out/../kernel 文件。
2. mkbootfs分析
mkbootfs在system/core/cpio/mkbootfs.c中
mkbootfs $OUT / root
main
--> archive(*argv, x); //*argv 要打包的源文件路径, x是打包后的文件路径
--> _archive_dir(in, out, strlen(in), strlen(out)); //in要打包的源文件路径, out是打包后的文件路径
mkbootfs $OUT
/
root
main
--> archive(*argv, x); //*argv 要打包的源文件路径, x是打包后的文件路径
--> _archive_dir(in, out, strlen(in), strlen(out)); //in要打包的源文件路径, out是打包后的文件路径
--> _archive //对普通文件 目录 链接分开处理
注: 符号链接
假设有这样的符号连接
ln -s /home/cong/Desktop/record.txt link
则调用readlink后,buf="/home/cong/Desktop/record.txt", size=sizeof("/home/cong/Desktop/record.txt");
即realink所读到的就是link所指向的路径的内容及长度
函数的作用是:把文件的信息与文件的内容写到ramdisk.img中去
mkbootfs $OUT / root
main
--> archive(*argv, x); //*argv 要打包的源文件路径, x是打包后的文件路径
--> _archive_dir(in, out, strlen(in), strlen(out)); //in要打包的源文件路径, out是打包后的文件路径
--> _archive //对普通文件 目录 链接分开处理
--> _eject //将文件信息与内容写入到ramdisk.img中去
最后写入一个结束标志:
注: CPIO 的结构包括:
110字节的Head(6 + 8 * 13)
不定长的文件名(文件名的长度是olen)
结束字符 \0
文件的内容
.... //重复上面4个
最后的文件名是一个 TRAILER!!!
附1. ramdisk.img的 解压过程
一. mkimage.sh
1. android目录下的mkimage.sh
- #!/bin/bash
- set -e
- . build/envsetup.sh >/dev/null && setpaths
- export PATH=$ANDROID_BUILD_PATHS:$PATH
- TARGET="withoutkernel"
- if [ "$1"x != ""x ]; then
- TARGET=$1
- fi
- rm -rf rockdev/Image
- mkdir -p rockdev/Image
//在init.rk30board.rc中搜索字符串mtd@system,发现其分区类型为ext4
- FSTYPE=`grep 'mtd@system' $OUT/root/init.rk30board.rc | head -n 1 | awk '{ print $2 }'`
- if [ "$FSTYPE" = "" ]; then
- FSTYPE=`grep 'mtd@system' $OUT/root/init.rc | head -n 1 | awk '{ print $2 }'`
- fi
- echo system filesysystem is $FSTYPE
- BOARD_CONFIG=device/rockchip/rk30sdk/device.mk
- //KERNLE_SRC_PATH为空
- KERNEL_SRC_PATH=`grep TARGET_PREBUILT_KERNEL ${BOARD_CONFIG} |grep "^\s*TARGET_PREBUILT_KERNEL *:= *[\w]*\s" |awk '{print $3}'`
- [ $(id -u) -eq 0 ] || FAKEROOT=fakeroot
- BOOT_OTA="ota"
- echo "TARGET=$TARGET"
- [ $TARGET != $BOOT_OTA -a $TARGET != "withoutkernel" ] && echo "unknow target[${TARGET}],exit!" && exit 0
- if [ ! -f $OUT/kernel ] //这个文件存在不执行以下
- then
- echo "kernel image not fount![$OUT/kernel] "
- read -p "copy kernel from TARGET_PREBUILT_KERNEL[$KERNEL_SRC_PATH] (y/n) n to exit?"
- if [ "$REPLY" == "y" ]
- then
- [ -f $KERNEL_SRC_PATH ] || \
- echo -n "fatal! TARGET_PREBUILT_KERNEL not eixit! " || \
- echo -n "check you configuration in [${BOARD_CONFIG}] " || exit 0
- cp ${KERNEL_SRC_PATH} $OUT/kernel
- else
- exit 0
- fi
- fi
//1.boot.img的生成过程
- if [ $TARGET == $BOOT_OTA ] //TARGET=withoutkernel, BOOT_OTA=ota不相等,执行else
- then
- echo "make ota images... "
- echo -n "create boot.img with kernel... "
- [ -d $OUT/root ] && \
- mkbootfs $OUT/root | minigzip > $OUT/ramdisk.img && \
- mkbootimg --kernel $OUT/kernel --ramdisk $OUT/ramdisk.img --output $OUT/boot.img && \
- cp -a $OUT/boot.img rockdev/Image/
- echo "done."
- else
- echo -n "create boot.img without kernel... "
- [ -d $OUT/root ] && \ //目录out/target/product/rk30sdk/root存在
- mkbootfs $OUT/root | minigzip > $OUT/ramdisk.img && \ //1.将root下的每个文件加上cpio头+每个文件的内容,打包成cpios格式
- rkst/mkkrnlimg $OUT/ramdisk.img rockdev/Image/boot.img //2. 将这个cpio文件用gzip压缩后写到文件ramdisk.img中, //3. mkkrnlimg会对ramdisk.img加上8个字节的头标志 4b 52 4e 4c e9 96 0f 00 ,尾部加上4个字节 02 6e e6 55。
- echo "done."
- fi
out/target/..../kernel 多 kernel/kernel.img 首4b 52 4e 4c 24 d0 6c 00 尾:fb 90 e5 ca
################boot.img = (out/ramdisk.img + mkkrnling加入头尾标)
=>out/ramdisk.img = out/root压缩.
=>out/root =空文件夹(data,dev,proc,sys,system)+res(里面几张电源图片)+sbin(里面带adbd,e2fsck mkdosfs mke2fs resize2fs ueventd,watchdogd等二进制文件) +charger,init可执行文件+一些文件,具体如下:
-rwxr-xr-x 1 android android 264100 2014-07-22 18:01 charger*
drwxr-xr-x 2 android android 4096 2014-07-22 16:10 data/
-rw-r--r-- 1 android android 905 2014-07-22 16:13 default.prop
drwxr-xr-x 2 android android 4096 2014-07-22 16:10 dev/
-rwxr-xr-x 1 android android 7667 2014-07-22 17:05 drmboot.ko*
-rwxr-xr-x 1 android android 113644 2014-07-22 18:01 init*
-rw-r--r-- 1 android android 2487 2014-07-22 17:05 init.goldfish.rc
-rwxr-xr-x 1 android android 20012 2014-07-24 17:27 init.rc*
-rw-r--r-- 1 android android 394 2014-07-22 17:05 init.rk30board.bootmode.emmc.rc
-rw-r--r-- 1 android android 327 2014-07-22 17:05 init.rk30board.bootmode.unknown.rc
-rwxr-xr-x 1 android android 11770 2014-07-22 17:05 init.rk30board.rc*
-rwxr-xr-x 1 android android 6122 2014-07-22 17:05 init.rk30board.usb.rc*
-rw-r--r-- 1 android android 1795 2014-07-22 17:05 init.trace.rc
-rw-r--r-- 1 android android 2562 2014-07-22 17:05 init.usb.rc
drwxr-xr-x 2 android android 4096 2014-07-22 16:10 proc/
drwxr-xr-x 3 android android 4096 2014-07-22 17:06 res/
-rwxr-xr-x 1 android android 150405 2014-07-22 17:05 rk30xxnand_ko.ko.3.0.36+*
-rwxr-xr-x 1 android android 145284 2014-07-22 17:05 rk30xxnand_ko.ko.3.0.8+*
drwxr-xr-x 2 android android 4096 2014-07-22 18:01 sbin/
drwxr-xr-x 2 android android 4096 2014-07-22 16:10 sys/
drwxr-xr-x 2 android android 4096 2014-07-22 16:10 system/
-rw-r--r-- 1 android android 272 2014-07-22 17:05 ueventd.goldfish.rc
-rwxr-xr-x 1 android android 4547 2014-07-22 17:05 ueventd.rc*
-rwxr-xr-x 1 android android 3106 2014-07-22 17:05 ueventd.rk30board.rc*
总结:boot.img由 out/.../root/里面的文件通过minizip压缩后,在经过rkkrnlimg 加入头尾标志而生成的。
boot_ota.img则多一个out/../kernel 文件。
- //2.recovery.img的生成过程
- echo -n "create recovery.img with kernel... "
- [ -d $OUT/recovery/root ] && \
- mkbootfs $OUT/recovery/root | minigzip > $OUT/ramdisk-recovery.img && \
- mkbootimg --kernel $OUT/kernel --ramdisk $OUT/ramdisk-recovery.img --output $OUT/recovery.img && \
- cp -a $OUT/recovery.img rockdev/Image/
- echo "done."
总结:recovery.img 由out/.../recovery/root 压缩并加入 out/.../kernel 得到的。 而recovery/root和 上面的root/里面对比多了几个文件而已。
// misc.img 的生成过程
- echo -n "create misc.img.... "
- cp -a rkst/Image/misc.img rockdev/Image/misc.img
- cp -a rkst/Image/pcba_small_misc.img rockdev/Image/pcba_small_misc.img
- cp -a rkst/Image/pcba_whole_misc.img rockdev/Image/pcba_whole_misc.img
- echo "done."
misc.img 一般用来游戏或软件的图像文件
-
- //3.system.img的生成过程
- if [ -d $OUT/system ]
- then
- echo -n "create system.img... "
- if [ "$FSTYPE" = "cramfs" ]
- then
- chmod -R 777 $OUT/system
- $FAKEROOT mkfs.cramfs $OUT/system rockdev/Image/system.img
- elif [ "$FSTYPE" = "squashfs" ]
- then
- chmod -R 777 $OUT/system
- mksquashfs $OUT/system rockdev/Image/system.img -all-root >/dev/null
- elif [ "$FSTYPE" = "ext3" ] || [ "$FSTYPE" = "ext4" ]
- then //ext3或ext4的生成过程
- delta=5120
- num_blocks=`du -sk $OUT/system | tail -n1 | awk '{print $1;}'`
- num_blocks=$(($num_blocks + $delta))
- num_inodes=`find $OUT/system | wc -l`
- num_inodes=$(($num_inodes + 500))
- ok=0
- while [ "$ok" = "0" ]; do
- genext2fs -a -d $OUT/system -b $num_blocks -N $num_inodes -m 0 rockdev/Image/system.img >/dev/null 2>&1 && \
- tune2fs -j -L system -c -1 -i 0 rockdev/Image/system.img >/dev/null 2>&1 && \
- ok=1 || num_blocks=$(($num_blocks + $delta))
- done
- e2fsck -fy rockdev/Image/system.img >/dev/null 2>&1 || true
- delta=1024
- num_blocks=`resize2fs -P rockdev/Image/system.img 2>&1 | tail -n1 | awk '{print $7;}'`
- rm -f rockdev/Image/system.img
- ok=0
- while [ "$ok" = "0" ]; do
- genext2fs -a -d $OUT/system -b $num_blocks -N $num_inodes -m 0 rockdev/Image/system.img >/dev/null 2>&1 && \
- tune2fs -O dir_index,filetype,sparse_super -j -L system -c -1 -i 0 rockdev/Image/system.img >/dev/null 2>&1 && \
- ok=1 || num_blocks=$(($num_blocks + $delta))
- done
- e2fsck -fyD rockdev/Image/system.img >/dev/null 2>&1 || true
- else
- mkdir -p rockdev/Image/2k rockdev/Image/4k
- mkyaffs2image -c 2032 -s 16 -f $OUT/system rockdev/Image/2k/system.img
- mkyaffs2image -c 4080 -s 16 -f $OUT/system rockdev/Image/4k/system.img
- fi
- echo "done."
- fi
- chmod a+r -R rockdev/Image/
总结:system.img 用的就是out/..../system/ 里面的的。1、先用genext2fs 将out/.../system/文件夹制作成一个ext2文件系统镜像。2、tune2fs是调整和查看ext2/ext3文件系统的文件系统参数,Windows下面如果出现意外断电死机情况,下次开机一般都会出现系统自检。Linux系统下面也有文件系统自检,而且是可以通过tune2fs命令,自行定义自检周期及方式。
3、 e2fsck 检查ext2、ext3、ext4等文件系统的正确性。
3、 e2fsck 检查ext2、ext3、ext4等文件系统的正确性。
mkbootfs在system/core/cpio/mkbootfs.c中
mkbootfs $OUT / root
main
--> archive(*argv, x); //*argv 要打包的源文件路径, x是打包后的文件路径
--> _archive_dir(in, out, strlen(in), strlen(out)); //in要打包的源文件路径, out是打包后的文件路径
- static void _archive_dir(char *in, char *out, int ilen, int olen)
- {
- int i, t;
- DIR *d;
- struct dirent *de;
- DIR* d = opendir(in); //打开目录
- int size = 32;
- int entries = 0;
- char** names = malloc(size * sizeof(char*)); //每次分配32个文件名的内存
- //将./out/target/product/rk30sdk/root目录下所有的文件名存在name数组中
- while((de = readdir(d)) != 0){
- if(de->d_name[0] == '.') continue; //跳过.目录
- if(!strcmp(de->d_name, "root")) continue; //排除root,这是为了什么?
- if (entries >= size) {
- size *= 2; //如果超出32个文件名,则再多分配32*2个,依次类推
- names = realloc(names, size * sizeof(char*)); //realloc,又学习了一招
- }
- names[entries] = strdup(de->d_name); //strdup可以分配内存并复制
- ++entries;
- }
- //对name数组中的文件名进行排序
- qsort(names, entries, sizeof(char*), compare);
- for (i = 0; i < entries; ++i) {
- t = strlen(names[i]);
- in[ilen] = '/';
- memcpy(in + ilen + 1, names[i], t + 1);
- if(olen > 0) { //out为空
- out[olen] = '/';
- memcpy(out + olen + 1, names[i], t + 1);
- _archive(in, out, ilen + t + 1, olen + t + 1);
- } else {
- memcpy(out, names[i], t + 1); //将文件名复制到out中
- _archive(in, out, ilen + t + 1, t); //将这个文件打包到ramdisk.img中
- }
- in[ilen] = 0;
- out[olen] = 0;
- free(names[i]);
- }
- free(names);
- }
main
--> archive(*argv, x); //*argv 要打包的源文件路径, x是打包后的文件路径
--> _archive_dir(in, out, strlen(in), strlen(out)); //in要打包的源文件路径, out是打包后的文件路径
--> _archive //对普通文件 目录 链接分开处理
- static void _archive(char *in, char *out, int ilen, int olen)
- {
- struct stat s;
- if(S_ISREG(s.st_mode)){ //1.对普通文件的处理
- int fd = open(in, O_RDONLY);
- char* tmp = (char*) malloc(s.st_size);
- read(fd, tmp, s.st_size);
- _eject(&s, out, olen, tmp, s.st_size);
- free(tmp);
- close(fd);
- } else if(S_ISDIR(s.st_mode)) { //2.对目录的处理
- _eject(&s, out, olen, 0, 0);
- _archive_dir(in, out, ilen, olen);
- } else if(S_ISLNK(s.st_mode)) { //3.对链接文件的处理
- char buf[1024];
- int size;
- size = readlink(in, buf, 1024); //调用readlink读符号链接的大小及内容
- _eject(&s, out, olen, buf, size);
- } else {
- die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
- }
- }
假设有这样的符号连接
ln -s /home/cong/Desktop/record.txt link
则调用readlink后,buf="/home/cong/Desktop/record.txt", size=sizeof("/home/cong/Desktop/record.txt");
即realink所读到的就是link所指向的路径的内容及长度
函数的作用是:把文件的信息与文件的内容写到ramdisk.img中去
mkbootfs $OUT / root
main
--> archive(*argv, x); //*argv 要打包的源文件路径, x是打包后的文件路径
--> _archive_dir(in, out, strlen(in), strlen(out)); //in要打包的源文件路径, out是打包后的文件路径
--> _archive //对普通文件 目录 链接分开处理
--> _eject //将文件信息与内容写入到ramdisk.img中去
- static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
- {
- static unsigned next_inode = 300000;
- while(total_size & 3) {
- total_size++;
- putchar(0);
- }
- fix_stat(out, s);
- //这儿的printf实际上是要写入到ramdisk.img中去的
- printf("%06x%08x%08x%08x%08x%08x%08x"
- "%08x%08x%08x%08x%08x%08x%08x%s%c",
- 0x070701, <<<<--- 标志
- next_inode++, // s.st_ino, //inode_number
- s->st_mode, //mode
- 0, // s.st_uid, //uid
- 0, // s.st_gid, //gid
- 1, // s.st_nlink, //链接数
- 0, // s.st_mtime, //最后修改时间
- datasize, //文件长度
- 0, // volmajor //主号
- 0, // volminor //次号
- 0, // devmajor //主设备号
- 0, // devminor, //次设备号
- olen + 1, //name_len名字长度
- 0, --->>>>
- out,
- 0
- );
- total_size += 6 + 8*13 + olen + 1;
- while(total_size & 3) {
- total_size++;
- putchar(0);
- }
- if(datasize) {
- fwrite(data, datasize, 1, stdout); //这是文件的内容
- total_size += datasize;
- }
- }
- static void _eject_trailer()
- {
- struct stat s;
- memset(&s, 0, sizeof(s));
- _eject(&s, "TRAILER!!!", 10, 0, 0);
- while(total_size & 0xff) {
- total_size++;
- putchar(0);
- }
- }
110字节的Head(6 + 8 * 13)
不定长的文件名(文件名的长度是olen)
结束字符 \0
文件的内容
.... //重复上面4个
最后的文件名是一个 TRAILER!!!
附1. ramdisk.img的 解压过程
- cong@ubuntu:/tmp/test$ file ./bak/ramdisk.img
- ./bak/ramdisk.img: gzip compressed data, from Unix
- cong@ubuntu:/tmp/test$ cp ./bak/ramdisk.img ./bak/ramdisk.img_bak.gz //重新copy一份
- cong@ubuntu:/tmp/test$ gunzip ./bak/ramdisk.img_bak.gz //gunzip解压缩
- cong@ubuntu:/tmp/test$ ls ./bak/
- ramdisk.img ramdisk.img_bak ramdisk.img.gz
- cong@ubuntu:/tmp/test$ file ./bak/ramdisk.img_bak //解压后的文件就是cpio格式的
- ./bak/ramdisk.img_bak: ASCII cpio archive (SVR4 with no CRC)
- cong@ubuntu:/tmp/test$ cpio -i -F ./bak/ramdisk.img_bak //解压cpio格式
- 463 blocks
- cong@ubuntu:/tmp/test$ ls //这就能看到所有文件了
- bak data default.prop dev forlinx.rc init init.goldfish.rc init.rc linuxrc proc sbin sys system ueventd.goldfish.rc ueventd.rc
- cong@ubuntu:/tmp/test$