Android x86镜像分析 | 您所在的位置:网站首页 › AndroidX86镜像下载 › Android x86镜像分析 |
这几天学习Android,基于x86平台。先了解一下android的安装过程。在其官方网站上下载了Android的iSO,就解压出来看看,需要说明的是以下的操作都是在root用户下进行的。以下是学习过程中,从网上找到的文件,这篇文章的主要参考链接为:https://www.cnblogs.com/jjxxjnzy/archive/2013/10/14/3368068.html,感谢! 1 2 mkdir android mount android-x86-4.3-20130725.iso android 通过以上的命令可以把我们下载的android镜像挂载到android目录下,看一目录结构: 1 2 3 4 5 6 7 8 9 10 11 12 13 ├── initrd.img ├── install.img ├── isolinux │ ├── android-x86.png │ ├── boot.cat │ ├── isolinux.bin │ ├── isolinux.cfg │ ├── TRANS.TBL │ └── vesamenu.c32 ├── kernel ├── ramdisk.img ├── system.sfs └── TRANS.TBL 对以上文件详细的了解可以参考csdn的这篇博文 1 https://blog.csdn.net/ldq_sd/article/details/104196923接下来看一下install.img这个文件,我们用file命令看一下这个文件属性 1 2 android# file install.img install.img: gzip compressed data, from Unix, last modified: Fri Jul 26 02:05:58 2013, max compression 可以看到这个文件是一个gzip的文件,所以我们可以解压出来看看,命令如下: 1 2 3 4 5 mkdir install cp /tmp/android/install.img /tmp/install mv install.img install.img.gz gunzip install.img.gz cpio -i -F install.img 这个时候我们看下install这个目录的结构: 1 2 install# ls bin grub install.img lib sbin scripts 可以看出通过这个文件的解压多出了以下目录: bin grub lib sbin scripts不过通过以上的目录可以看出,基于x86结构的android是用grub引导的。接下来看一下他的启动流程,在看启动流程之前我们先看一下isolinux.cfg这个文件,这个文件的具体作用在上面的内容中已经说过,在此不在赘述,文件内容如下: default vesamenu.c32 timeout 600 menu background android-x86.png menu title Android-x86 Live & Installation CD 4.3-test menu color border 0 #ffffffff #00000000 menu color sel 7 #ffffff00 #ff000000 menu color title 0 #ffffffff #00000000 menu color tabmsg 0 #ffffffff #00000000 menu color unsel 0 #ffffffff #00000000 menu color hotsel 0 #ffffff00 #ff000000 menu color hotkey 7 #ffffff00 #00000000 label livem menu label Live CD - ^Run Android-x86 without installation kernel /kernel append initrd=/initrd.img root=/dev/ram0 androidboot.hardware=android_x86 video=-16 quiet SRC= DATA= label vesa menu label Live CD - ^VESA mode kernel /kernel append initrd=/initrd.img root=/dev/ram0 androidboot.hardware=android_x86 video=-16 quiet nomodeset vga=788 SRC= DATA= label debug menu label Live CD - ^Debug mode kernel /kernel append initrd=/initrd.img root=/dev/ram0 androidboot.hardware=android_x86 video=-16 vga=788 DEBUG=2 SRC= DATA= label install menu label Installation - ^Install Android-x86 to harddisk kernel /kernel append initrd=/initrd.img root=/dev/ram0 androidboot.hardware=android_x86 video=-16 INSTALL=1 DEBUG= 我们主要看下面的label这几个选项。一共有四个label选项,前面的两个不太熟悉,不过在这里不影响我们。第三个为debug模式,第四个为install模式,也就是android的安装,从加在这里的内核参数可以看出,系统启动的是initrd.img里的内容,那接下来我们就分析一下initrd.img 这个文件。我们用file命令看一下这个文件的属性就可以知道他和上文说的install.img的文件属性是一样的,我们用同样的方法把他解压出来,内容如下: initrd# ls android bin hd init lib mnt proc sbin scripts sfs sys tmp以上就是把initrd.img文件解压出来的内容,系统的启动就是从这个地方开始,熟悉linux的朋友看到这个就知道是从那个文件开始执行了。接下来我们看一下init这个文件,这个文件是一个文本文件,可以看一下它的文件属性 1 2 initrd# file init init: a /bin/busybox sh script, ASCII text executable 下面可以看一下这个脚本的内容: 1 2 3 4 5 6 7 8 9 10 11 12 echo -n Detecting Android-x86...
mount -t tmpfs tmpfs /android cd /android while :; do for device in ${ROOT:-/dev/sr* /dev/[hs]d[a-z]*}; do check_root $device && break 2 mountpoint -q /mnt && umount /mnt done sleep 1 echo -n . done 这个是脚本的初始函数,相当于c语言的mian函数,首先看到的地一个mount命令挂载了tmpfs文件系统,对这个不是很熟悉,等有时间了学习了一下这个在把这个补上。看看接下来while循环,在这一段内容里重点看一下 check_root,这个是定义的一个函数,这个函数定义如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 try_mount() { RW=$1; shift if [ "${ROOT#*:/}" != "$ROOT" ]; then # for NFS roots, use nolock to avoid dependency to portmapper RW="nolock,$RW" fi # FIXME: any way to mount ntfs gracefully? mount -o $RW $@ || mount.ntfs-3g -o rw,force $@ }
check_root() { try_mount ro $1 /mnt && [ -e /mnt/$SRC/ramdisk.img ] [ $? -ne 0 ] && return 1 zcat /mnt/$SRC/ramdisk.img | cpio -id > /dev/null if [ -e /mnt/$SRC/system.sfs ]; then mount -o loop /mnt/$SRC/system.sfs /sfs mount -o loop /sfs/system.img system elif [ -e /mnt/$SRC/system.img ]; then mount -o loop /mnt/$SRC/system.img system elif [ -d /mnt/$SRC/system ]; then remount_rw mount --bind /mnt/$SRC/system system else rm -rf * return 1 fi mkdir cache mnt mnt/sdcard mount -t tmpfs tmpfs cache echo " found at $1" } 这个函数的作用主要是挂载ramdisk.img和system.sfs,这两个文件可以看看我们最初始解压的文件。这里需要说明的是system.sfs这个文件解压出来是system.img。system.img里包含的就是具体的系统文件了。这个到后面会讲到。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ln -s mnt/$SRC /src ln -s android/system / ln -s ../system/lib/modules /lib ln -s ../system/lib/firmware /lib
if [ -n "$INSTALL" ]; then cd / zcat /src/install.img | cpio -iud > /dev/null fi
if [ -x system/bin/ln -a \( -n "$DEBUG" -o -n "$BUSYBOX" \) ]; then mv /bin /lib . system/bin/ln -s android/lib /lib system/bin/ln -s android/bin /bin sed -i 's|\(PATH *\)\(/sbin\)|\1/bin:\2|' init.rc mv /sbin/* sbin rmdir /sbin ln -s android/sbin / fi
# ensure keyboard driver is loaded [ -n "$INSTALL" -o -n "$DEBUG" ] && modprobe atkbd
if [ 0$DEBUG -gt 0 ]; then echo -e "\nType 'exit' to continue booting...\n" debug_shell debug-found fi
# load scripts for s in `ls /scripts/* /src/scripts/*`; do test -e "$s" && source $s done 下图为system.img文件内容:
app 主要存放的是常规下载的应用程序,可以看到都是以APK格式结尾的文件,在这个文件夹下的程序为系统默认的组件,自己安装的软件将不会出现在这里,而是\data\文件夹中。 应用举例: \AlarmClock.apk 闹钟 \Browser.apk 浏览器 \Bugreport.apk 错误报告 \ Calculator.apk 计算器 bin 目录下的文件都是系统的本地程序,是binary二进制的程序,主要是Linux系统自带的组件: \app_process 系统进程 \dalvikvm Dalvik虚拟机宿主 \dbus-daemon 系统BUS总线监控 \debuggerd 调试器 \debug_tool 调试工具 \dexopt DEX选项 \dhcpcd DHCP服务器 etc 配置文件,如: \bluetooth 蓝牙设备配置文件 fonts 字体,中文字库,unicode字库等 \fonts\DroidSans-Bold.ttf \fonts\DroidSansFallback.ttf framework 平台框架,如: \am.jar \am.odex \android.awt.jar AWT库 \android.awt.odex lib 系统运行库,如: \libaes.so \libagl.so \libandroid_runtime.so Android运行时库 \libandroid_servers.so 系统服务组件 \liba io.so 音频处理 \liba ioeq.so EQ均衡器 \liba ioflinger.so 音频过滤器 \libbltooth.so 蓝牙组件 media 各种铃声,系统提示音等 usr 用户文件夹,包含共享、键盘布局、时间区域文件等。 \keychars \keylayout \share \srec 在文件系统中,android4层架构显现的很明显:app应用,framework框架,lib运行库。android系统4层架构(应用层,框架层,运行库层,内核层)之间,应用层通过框架对使用到库进行调用、内核负责内存管理进程调度等。通过在不同层上的裁剪可以实现部分功能的移出。例如:应用层上移出APK,相应应用无法使用;运行库层移出运行所需的库文件,所有调用该库文件均报错不能执行;在内核层上,通过config编译内核也可达到同样目的。 以上做的就是一些初始化的工作,并且根据不同启动参数,所做的操作也略有不同。可以看看上面标出的红色部分,此处的主要作用是如果判断出系统启动时所带的参数为install,则进入安装模式。接着往下看 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 [ "$AUTO" != "1" ] && detect_hardware && FOUND=1
[ -n "$INSTALL" ] && do_install
load_modules mount_data mount_sdcard setup_tslib setup_dpi post_detect find_network_dev_name
if [ 0$DEBUG -gt 1 ]; then echo -e "\nUse Alt-F1/F2/F3 to switch between virtual consoles" echo -e "Type 'exit' to enter Android...\n"
debug_shell debug-late fi
[ -n "$DEBUG" ] && SWITCH=${SWITCH:-chroot}
# We must disable mdev before switching to Android # since it conflicts with Android's init echo > /proc/sys/kernel/hotplug
exec ${SWITCH:-switch_root} /android /init
# avoid kernel panic while :; do echo echo ' Android-x86 console shell. Use only in emergencies.' echo debug_shell fatal-err done 可以看出上面的代码用红色标注了两处,其中第一处是用来判断是否是install模式,在这个地方需要注意下,如果是正常的启动此处是不会执行到的,如果是安装模式则会调用安装脚本,这个安装脚本是放在install.img 这个文件里。这个文件的目录在上文我已经列过了。具体的看看do_install这个函数,这个函数是定义在install.img 解压出文件的scripts/1-install这个文件当中,在这个脚本中为什么能调用这个函数呢,注意这一句: 1 2 3 4 # load scripts for s in `ls /scripts/* /src/scripts/*`; do test -e "$s" && source $s done 这一段代码遍历了/scripts/和 /src/scripts/这两个目录下的脚本文件。注意后面的source $s,正是有了这一句,我们才可以在在init脚本里调用1-install这个脚本的函数。这个install.sh就是实现了我们的安装功能。 install.img文件中,我们先看一下这个包里有那些文件,在这里我以树的形式列出包含有那些文件: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 install# tree . ├── bin │ ├── cfdisk │ ├── dialog │ └── pv ├── grub │ ├── android-x86.xpm.gz │ ├── e2fs_stage1_5 │ ├── fat_stage1_5 │ ├── iso9660_stage1_5 │ ├── ntfs_stage1_5 │ ├── stage1 │ ├── stage2 │ └── stage2_eltorito ├── lib │ ├── libncursesw.so.5 │ ├── libntfs.so.10 │ ├── libtinfo.so.5 │ ├── libuuid.so.1 │ └── terminfo │ └── l │ └── linux ├── sbin │ ├── grub │ └── mkntfs └── scripts └── 1-install 这个函数包含在1-install这个文件里,可以看一下这个函数的实现: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 do_install() { until install_hd; do if [ $retval -eq 255 ]; then dialog --title ' Error! ' --yes-label Retry --no-label Reboot \ --yesno '\nInstallation failed! Please check if you have enough free disk space to install Android-x [ $? -eq 1 ] && rebooting fi done
[ -n "$VESA" ] || runit="Run Android-x86" dialog --clear --title ' Congratulations! ' \ --menu "\n Android-x86 is installed successfully.\n " 11 51 13 \ "$runit" "" "Reboot" "" 2> $tempfile case "`cat $tempfile`" in Run*) cd /android umount system if mountpoint -q /sfs; then umount /sfs mount -o loop /hd/$asrc/system.sfs /sfs mount -o loop /sfs/system.img system else mount -o loop /hd/$asrc/system.img system fi if [ -d /hd/$asrc/data ]; then mount --bind /hd/$asrc/data data elif [ -e /hd/$asrc/data.img ]; then mount -o loop /hd/$asrc/data.img data fi ;; *) rebooting ;; esac 在这个函数中使用了until这样一个关键字,这个是shell脚本的语句,具体的作用大家google吧,接下来调用了install_hd这个函数,这个函数也是在这个文件里定义的,具体实现如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 install_hd() { select_dev || rebooting retval=1 case "$choice" in [sh]d*) install_to $choice retval=$? ;; Create*) cfdisk ;; Detect*) dialog --title " Detecting... " --nocancel --pause "" 8 41 1 ;; esac return $retval } 在这个函数里可以看到调用了一个select_dev的函数,这个函数也定义在该文件中,定义如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 select_dev() { fdisk -l | grep ^/dev | cut -b6-12,55- | awk '{ if (!match($2, "Extended")) { printf("\"%-28s", $0) system("echo -n `cat /sys/block/*/"$1"/../device/model`") printf("\" \"\"\n") } } END { printf("\"Create/Modify partitions\" \"\"\n\"Detect devices\" \"\"") }' > $menufile choose "Choose Partition" "Please select a partition to install Android-x86:" return $retval } 在上面的函数中还调用了另外一个函数,choose,该函数定义如下: 1 2 3 4 5 6 7 8 choose() { dialog --clear --title " $1 " \ --menu "$2" 20 71 13 --file $menufile 2> $tempfile
retval=$? choice=`cat $tempfile` } 在这个函数中需要注意的是最后一个choice值,这个值在后面会用到。接下来我们回到install_hd,这个函数,继续往下执行: 1 2 3 4 5 6 7 8 9 10 11 12 case "$choice" in [sh]d*) install_to $choice retval=$? ;; Create*) cfdisk ;; Detect*) dialog --title " Detecting... " --nocancel --pause "" 8 41 1 ;; esac 因为此处我们主要讲解安装,所以其他的来那个选项我们暂时不分析了,等后面在补上。我们看一下上述表红色的部分。其中retval=$?用来取得调用select_dev函数的返回值。接下来我们看一下install_to这个函数,这个函数也是定义在这个文件里,由于这个函数内容比较多,我们一段一段分析: 1 2 3 4 5 6 7 8 9 cd / mountpoint -q /hd && umount /hd while [ 1 ]; do format_fs $1 try_mount rw /dev/$1 /hd && break dialog --clear --title " Error " --defaultno --yesno \ "\n Cannot mount /dev/$1\n Do you want to format it?" 8 37 [ $? -ne 0 ] && return 255 done 这一段代码的主要功能是完成对磁盘的分区与格式化,具体的调用是在format_fs中实现,该函数实现如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 format_fs() { local cmd echo -e '"Do not format" ""\next3 ""\next2 ""\nntfs ""\nfat32 ""' > $menufile choose "Choose filesystem" "Please select a filesystem to format $1:" case "$choice" in ext3) cmd="mke2fs -jL" ;; ext2) cmd="mke2fs -L" ;; ntfs) cmd="mkntfs -fL" ;; fat32) cmd="newfs_msdos -L" ;; *) ;; esac if [ -n "$cmd" ]; then dialog --title " Confirm " --no-label Skip --yesno \ "\n You chose to format $1 to $choice.\n All data in that partition will LOSE.\n\n Are you sure to format the partition $1?" 10 51 [ $? -ne 0 ] && return 1 $cmd Android-x86 /dev/$1 | awk '{ # FIXME: very imprecise progress if (match($0, "done")) printf("%d\n", i+=33) }' | progress_bar "Formatting" "Formatting partition $1..." fi } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fs=`cat /proc/mounts | grep /dev/$1 | awk '{ print $3 }'`
asrc=android-$VER dialog --title " Confirm " --no-label Skip --defaultno --yesno \ "\n Do you want to install boot loader GRUB?" 7 47 if [ $? -eq 0 ]; then cp -af /grub /hd d=0 while [ 1 ]; do h=`echo $d | awk '{ printf("%c", $1+97) }'` [ -d /sys/block/[sh]d$h/$1 ] && break d=$(($d+1)) done p=$((`echo $1 | cut -b4-`-1)) create_menulst $p create_winitem $1 $d rm -f /hd/boot/grub/stage1 echo "setup (hd$d) (hd$d,$p)" | grub > /dev/tty5 [ $? -ne 0 ] && return 255 fi
|
CopyRight 2018-2019 实验室设备网 版权所有 |