这几天可能要接个Android的项目,基于x86平台。所以先了解一下android的安装过程。在其官方网站上下载了Android的img,先面就解压出来看看吧,需要说明的是以下的操作都是在root用户下进行的。

mkdir android
mount android-x86-4.3-20130725.iso  android

通过以上的命令可以把我们下载的android镜像挂载到android目录下,下面我们可以看一下他的目录结构:

├── 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的这篇博文

http://blog.csdn.net/liujixin8/article/details/4029887

接下来看一下install.img这个文件,我们用file命令看一下这个文件属性

android# file install.img 
install.img: gzip compressed data, from Unix, last modified: Fri Jul 26 02:05:58 2013, max compression

可以看到这个文件是一个gzip的文件,所以我们可以解压出来看看,命令如下:

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这个目录的结构:

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这个文件,这个文件是一个文本文件,可以看一下它的文件属性

initrd# file init 
init: a /bin/busybox sh script, ASCII text executable

下面可以看一下这个脚本的内容:

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,这个是定义的一个函数,这个函数定义如下:

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里包含的就是具体的系统文件了。这个到后面会讲到。

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

以上做的就是一些初始化的工作,并且根据不同启动参数,所做的操作也略有不同。可以看看上面标出的红色部分,此处的主要作用是如果判断出系统启动时所带的参数为install,则进入安装模式。接着往下看

[ "$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这个文件当中,在这个脚本中为什么能调用这个函数呢,注意这一句:

# 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就是实现了我们的安装功能。我们将在下一篇中分析do_install 这个函数