Android启动篇

来源:互联网 发布:眼下有细纹怎么办 知乎 编辑:程序博客网 时间:2024/06/05 05:36

        Android启动过程相当复杂,从引导器加载系统映像、通过init.rc脚本进行初始化配置到系统完全启动均属于启动过程的范畴。在系统启动过程中,根据系统内存的情况,还涉及垃圾回收、进程终止等内容;就单个应用的启动而言,涉及APK包解析、证书校验、权限检查等内容。

1.系统的启动过程

        在Android中,在BootLoader加载系统映像后,会通过system\core\rootdir\目录下的init.rc脚本进行初始化配置。在init.rc中可以配置系统时区、设置日志等级、设置全局环境变量、挂载文件系统、初始化网络配置、配置系统属性、启动守护进程等,具体的实现过程如下图所示:

               

        启动过程中的配置是系统正常运行的基本保证。

        (1)系统属性配置

                系统属性包括多个方面,在开机启动时,Android将进行一系列的配置。

                配置系统时区:在系统启动之初,最重要的工作自然是进行环境配置,其中最重要的配置是时间配置。Android默认设置系统时区为GMT 0。设置系统时区的方法是:sysclktz 0        //北京位于东八区,北京时间为GTM 8

                设置日志登记:在Android中,日志分为多个等级,包括2(VERBOSE)、3(DEBUG)、4(INFO)、5(WARN)、6(ERROR)、7(ASSERT)。通常并不是所有日志均具有重要价值,因此必须设置控制台输出的日志等级,默认为3。设置日志登记为3的方法是:loglevel 3。

                设置全局变量:在系统的启动过程中,Android需要设置的全局变量包括PATH、LD_LIBRARY_PATH、ANDROID_BOOTLOGO、ANDROID_ROOT、ANDROID_ASSETS、ANDROID_DATA、EXTERNAL_STORAGE、ASEC_MOUNTPOINT、LOOP_MOUNTPOINT和BOOTCLASSPATH等,具体如下:

                        export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin

                        export LD_LIBRARY_PATH /vendor/lib:/system/lib

                        export ANDROID_BOOTLOGO 1

                        export ANDROID_ROOT /system

                        export ANDROID_ASSETS /system/app        /加载应用的路径

                        export ANDROID_DATA /data

                        export EXTERNAL_STORAGE /mnt/sdcard

                        export ASEC_MOUNTPOINT /mnt/asec

                        export LOOP_MOUNTPOINT /mnt/obb

                        export BOOTCLASSPATH /system/framework/core.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/core-junit.jar

                初始化网络配置:在Android中,网络配置包括对io和其他网络接入点的信息进行配置,配置网络参数的工具是ifup,具体方法是:

                        ifup lo

                        hostname localhost

                        domainname localdomain

                        lo的网络参数配置完成后的其他相关问题可查阅init.goldfish.rc。

                配置系统属性:在Android中,系统属性是非常重要的一个概念,可以在编译脚本中对其进行设置。ActivityManagerService中用到的一些配置oom_adj值的系统属性如下:

                        setprop ro.POREGROUND_APP_ADJ 0        //前台进程

                        setprop ro.VISIBLE_APP_ADJ 1        //可视进程

                        setprop ro.PERCEPTIBLE_APP_ADJ  2        //感知进程

                        setprop ro.HEAVT_WEIGHT_APP_ADJ 3        //重量级进程

                        setprop ro.SECONDARY_SERVER_ADJ 4        //后台服务

                        setprop ro.BACKUP_APP_ADJ 5        //备份进程

                        setprop ro.HOME_APP_ADJ 6        //启动器

                        setprop ro.HIDDEN_APP_MIN_ADJ 7        //隐藏进程

                        setprop ro.EMPTY_APP_ADJ 15        //空进程

                oom_adj值通常被Android特有的内存管理驱动Low memory killer使用,它会在系统内存低于设定值时释放相应进程,保证系统的稳定运行。Low memory killer根据两个原则(进程的重要性和释放这个进程可获取的空闲内存数量)来决定释放的进程。oom_adj值越小,表示该类型的重要性越高。在oom_adj相同的情况下,占用内存大的进程优先被撤销,进程占用的内存可以通过get_mm_rss进行判断。

        (2)文件系统挂载

                配置好文件系统分区并设置好分区表后,在设备实际启动前,需要挂载文件系统。

                1)创建挂载点并设置权限

                        完成环境变量设置后,接下来就是创建挂载点并根据安全需要设置相应的权限,这是非常重要的一步。对于敏感信息,应避免普通用户对其拥有可写甚至可读的权限。权限的主要设置方法如下:

                                mkdir /mnt 0775 root system

                                mkdir /mnt/sdcard 0000 system system

                                symlink /mnt/sdcard /sdcard

                                mkdir /system

                                mkdir /data 0771 system system

                                mkdir /cache 0770 system cache

                                mkdir /config 0500 root root

                        从以上设置可以看出,Android拥有两个比较重要的用户权限,即root、system,其中root权限是最高的,对于系统和读写的路径至少应设置system权限。事实上目前刷机盛行,虽然OEM设置诸多屏障,但是仍有人能轻易获得Android的root权限,甚至还开发出了专门破解root权限的应用,这在一定程度上使Android的安全性让人担忧。

                2)挂载文件系统

                        挂载点配置完成后,就该挂载文件系统了。目前Android默认的几个文件系统为system、date、cache等,挂载文件系统的方法如下:

                                mount yaffs2 mtd@system /system

                                mount yaffs2 mtd@system /system ro remount

                                mount yaffs2 mtd@userdata /data nosuid nodev

                                mount yaffs2 mtd@cache /cache nosuid nodev

                        在默认情况下,Android支持的文件系统类型为yaffs2(当然针对/tmp,Android采用的是tmpfs文件系统,此处为狭义理解),当然OEM厂商可以根据自己的需要自行制作其他类型的文件系统进行挂载。

                        至于分区大小,各厂商可根据自己的实际情况确定。

        (3)守护进程启动

                守护进程时运行在后台的Android核心的进程,主要包括servicemanager、vold、netd、debuggerd、ril-daemon、zygote、drm、drmio、media、bootanim、dbus、bluetoothd、hfag、hsag、opush、pbap、installd、flash_recovery、racoon、mtpd、keystore、dumpstate等。

                1)守护进程的配置

                         由于不同守护进程之间可能存在依赖关系,或者守护进程对其他配置存在依赖,在启动守护进程时,需要做些配置。在system\core\init\目录下的readme.txt中介绍了包括守护进程在内的启动项配置方法,其中守护进程的配置方法如下:

                                 service <name> <pathname> [<argument>] *

                                         <option>

                                         <option>

                                 Critical:关键服务,如果这类服务在4分钟内4次退出,系统将重启进入恢复模式。

                                 Disabled: 服务不能根据类名自动重启,必须显示启动。

                                 setenv<name> <value>:设置环境变量。

                                 socket<name><type><perm>[<user>[<group>]]:创建套接字,type可以为dgram、stream、seqpacket、user和group默认为0.

                                 user<username>:服务所属的用户名,默认为root。

                                 group<groupname>[<groupname>]*:服务所属的组名,默认为root。

                                 oneshot:当服务重启时,执行相应的命令。

                                 class<name>:服务的类名,归属于同一类的服务必须一起启动或关闭,默认的类为default。

                2)守护进程的启动

                        (1)servicemanager

                                  servicemanager是系统服务的管理器,它通过一个HashMap<String, IBinder>来管理系统服务。当servicemanager重启时,会导致zygote和media重启。下面是servicemanager的启动配置:

                                          service servicemanager /system/bin/servicemanager

                                                  user system

                                                  critical

                                                  onrestart restart zygote

                                                  onrestart restart media

                        (2)vold的配置

                                vold(volume daemon)主要用来处理热插拔,其实际上是负责完成系统的CDROM、USB大容量存储和MMC卡等扩展存储的挂载任务的守护进程。vold和linux标准的udev类似,均通过sysfs的内核和用户层提供通信。下面是vold的启动配置:

                                        service vold /sysyem/bin/vold

                                                socket vold stream 0660 root mount

                                                ioprio be 2

                                热插拔的处理框架如下图:

                                       

                                和vold相关的代码主要位于system/vold目录下,有兴趣的读者可以做进一步的研究。

                        (3)netd

                                netd(network daemon)主要用来监控网络状态,进行网络管理,其实现位于system\netd目录下,其启动配置如下:

                                        service netd /system/bin/netd

                                                socket netd stream 0660 root system

                                                socket dnsproxyd stream 0660 root inet

                        (4)debuggerd

                                debuggerd(debugger daemon)主要用于调试,启动后会监听UNIX套接字Android:deguggerd,其实现位于system\core\debuggerd目录下,其启动配置如下:

                                        service debuggerd /system/bin/debuggerd

                        (5)ril-daemon

                                RIL守护进程会初始化芯片厂商的RIL,管理所有来自Android通信服务的通信,通过套套接字实现与芯片厂商的RIL的通信,其实现与hardware/ril/rild无关,其启动配置如下:

                                        service ril-daemon /system/bin/rild

                                                socket rild stream 660 root radio

                                                socket rild-debug stream 660 radio system

                                                user root

                                                group radip cache inet misc audio sdcard_rw

                        (6)zygote

                                 zygote服务是Android启动后启动的第一个Linux进程,其他的Linux进程,比如各个应用,均是由zygote产生的。关于zygote的实现可以参考ZygoteInit.java。zygote服务的重启会导致media和netd服务的重启,其启动配置如下:

                                        service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start -system-server

                                        socket zygote stream 666

                                        onrestart write /sys/android_power/request_state wake

                                        onrestart write /sys/power/state on

                                        onrestart restart media

                                        onrestart restart netd

                        (7)drm

                                drm 用于数字版权保护,其启动配置如下:

                                        service drm /system/bin/drmserver

                                                user drm

                                                group system root inet

                        (8)drmio

                                drmio同样用于数字版权保护,其启动配置如下:

                                        service drmio /system/bin/drmioserver

                                                user drmio

                        (9)media

                                media服务是提供多媒体服务的守护进程,它会启动AudioFlinger、MediaPlayerService、CameraService、AudioPolicyService等服务。media服务的入口实现如下:

                                        int main(int argc, char**argv)

                                        {

                                                sp<PrecessState> proc(ProcessState::self());

                                                sp<IServiceManager> sm=defaultServiceManager();

                                                AudioFlinger::instantiate();

                                                MediaPlayerService::instantiate();

                                                CameraService::instantiate();

                                                AudioPolicyService::instantate();

                                                ProcessState::self()->startThreadPool();

                                                IPCThreadState::self()->joinThreadPool();

                                        }

                                media服务的启动配置如下:

                                        service media /system/bin/mediaserver

                                                user media

                                                group system audio camera graphics inet net_bt net_bt_admin net_raw

                                                ioprio rt 4

                        (10)bootanim

                                boottanim服务即所谓的开机动画服务,其具体实现位于BootAnimation.cpp中。SurfaceFlinger在完成准备工作后会在其readToRun()方法中通过property_set("ctl.start", "bootanim")启动bootanim服务。在bootanim服务启动的过程中,会加载用户开机动画和系统开机动画,这些动画均为ZIP压缩文件,所在位置为\data\local\bootanimation.zip和\system\media\bootanimation.zip,动画中图片的格式应为RGB565,其实现位于frameworks\base\cmds\bootanimation目录中。bootanim服务的启动配置如下:

                                        service bootanim /system/bin/bootanimation

                                                user graphics

                                                group graphics

                                                disabled

                                                oneshot

                        (11)dbus

                                dbus和OpenBinder一样是进程间的通信机制,在Linux中dbus应用的非常广泛,如著名的Linux桌面环境Qt和Gnome。在Android中,进程间通信主要利用的是OpenBinder,dbus仅用于蓝牙协议栈Bluez中。dbus的启动配置如下:

                                        service dbus /system/bin/dbus-daemon --system --nofork

                                               socket dbus stream 660 bluetooth bluetooth

                                               user bluetooth

                                               group bluetooth net_bt_admin

                        (12)bluetoothd

                                bluetoothd是Bluez的守护进程,默认是不启动的。bluetoothd的启动配置如下:

                                        service bluetoothd /system/bin/bluetoothd -n

                                                socket bluetooth stream 660 bluetooth bluetooth

                                                socket dbus_bluetooth stream 660 bluetooth bluetooth bluetooth

                                                group bluetooth net_bt_admin_misc

                                                disabled

                        (13)hfag

                                hfag也用于Bluez,作用是启动蓝牙免提音频网关(Bluetooth Handsfree Audio Gateway)。hfag默认是不启动的,其启动配置如下:

                                        service hfag /system/bin/sdptool add --channel=10 HFAG

                                                user bluetooth

                                                group bluetooth net_bt_admin

                                                disabled

                                                oneshot

                        (14)hsag

                                hsag也用于Bluez,作用是启动蓝牙耳机音频网关(Bluetooth headset audio gateway)。hsag默认是不启动的,其启动配置如下:

                                        service hsag /system/bin/sdptool add --channel=11 HSAG

                                                user bluetooth

                                                groupbluetooth net_bt_admin

                                                disabled

                                                oneshot

                        (15)opush

                                opush即OBM Push,实现了Exchange ActiveSync服务器协议,同样也用于Bluez。opush默认是不启动的,其启动配置如下:

                                        service opush /system/bin/sdptool add --channel=12 OPUSH

                                                user bluetooth

                                                group bluetooth net_bt_admin

                                                disabled

                                                oneshot

                        (16)pbap

                                pbap即电话簿访问协议(Phonebook Access Profile),同样也用于Bluez。pbap默认是不启动的,其启动配置如下:

                                        service pbap /system/bin/sdptool add --channel=19 PBAP

                                                user bluetooth

                                                group bluetooth net_bt_admin

                                                disabled

                                                oneshot

                        (17)installd

                                install即安装守护进程,用于APK的安装,其启动配置如下:

                                        service installd /system/bin/installd

                                                socket installd stream 600 system system

                        (18)flash_recovery

                                flash_recovery用于系统发生故障时的恢复,其启动配置如下:

                                        service flash_recovery /system/etc/install_recovery.sh

                                                oneshot

                        (19)racoon

                                racoon是VPN的守护进程,其启动配置如下:

                                        service racoon /system/bin/racoon

                                                socket racoon stream 600 system system group net_admin

                                                disabled

                                                oneshot

                        (20)mtpd

                                mtpd是媒体传输协议(Media Transfer Protocol, MTP)的守护进程,其启动配置如下:

                                        service mtpd /system/bin/mtpd

                                                scoket mtpd stream 600 system system

                                                user vpn

                                                group vpn net_admin net_raw

                                                disabled

                                                oneshot

                        (21)keystore

                                keystore和数字签名证书有关,其启动配置如下:

                                        service dumpstate /system/bin/dumpstate -s

                                                socket dumpstate stream 0660 shell log

                                                disabled

                                                oneshot

2.应用的启动过程

        AAPT(Android Asset Packaging Tool)允许开发者查看、创建、更新与ZIP兼容的压缩文件(ZIP、JAR、APK),同时还将资源编译到断言(assert)中。

        通过WinRAR查看APK包可以看到,APK包大致包括res、AndroidManifest.xml、classes.dex、resource.arsc、META-INF和libs等几项。其中res文件夹包含的是资源文件,res文件夹下的XML是已经编译过的,减小了存储空间。AndroidManifest.xml为应用的配置文件,但是已经编译过。而classes.dex为应用的DEX字节码文件,是CLASS字节码去除冗余信息后的集合。resource.arsc为资源文件,在Android 2.3前采用的是UTF-16编码,在Android 2.3及以后的版本中,采用的是UTF-8编码,这样改变以为这在Android 2.3及以后版本中,基于英文应用的APK包会比原来的小,而基于中文应用的APK包会比原来大些。这是因为在UTF-16编码中,中文和英文均采用了2字节编码,而在UTF-8中,英文占用1字节,而中文要占用3字节。在META-INF中存放的是Android的数字签名证书。在libs中存放的通常是JAR和原生代码生成的共享库文件。

        需要说明的是,在Android中,采用的Java混淆器为开源的ProGuard 4.4。ProGuard可以在一定程度上防止别有用意的人的窥视,但对于需要保护的敏感信息,其安全性是无法保证的,因为专业人员极易渗透其信息含义。所以,Android发展至今,盗版应用泛滥,这对于保护开发者的知识产权和商业机密十分不利。如果需要增强实现的安全性,建议将敏感信息放置在原生代码中实现,尽量不要在Java代码中实现。原生代码和Java代码的互操作需要通过JNI进行。

        Android生成的Java字节码为DEX字节码,而非传统的CLASS字节码,但是在编译过程中,Android会先将Java文件编译为CLASS字节码,然后再将CLASS字节码转化为DEX字节码。商业因素是制定这一策略的主要因素。

        DEX字节码借鉴了Linux的BusyBox和Qt中的SingleBuild编译模式采用的最大限度复用信息的设计思想,能够有效地减少生成文件的大小。

        Android不允许安装同名的不同程序,否则会提示证书有误。

        (1)应用的启动配置

                对于所有应用程序而言,均有唯一的入口,在Java和C/C++中,这个入口均为main函数。

                在Android中,从AndroidManifest.xml中可以看到,Android应用由一个继承了ContextWrapper的Application构成,其中Application有Activity、Service、Receiver、Provider、user-library等组件构成。

                Android应用程序的组件之间的关系如下图:

                       

                需要注意的是,应用程序并非必须拥有Activity,如果仅是后台程序,那么仅有Application也是可以的。下面是一个基于Service的应用的实现:

                        public class NfcService extends Application{

                                public void onCreate(){

                                        super.onCreate();

                                }

                                public void onTerminate(){

                                        super.onTerminate();

                                }

                        }

                在AndroidManifest.xml文件中,对应用程序的定义如下:

                        <?xml version="1.0" encoding="utf-8"?>

                        <manifest xmlns:android=http://schemas.android.com/apk/res/android

                                package="com.android.nfc"

                                android:sharedUserId="android.uid.nfc"

                                android:sharedUserLabel="@string/nfcUserLabel">

                                <application android:name=".NfcService"

                                        android:icon="@drawable/icon"

                                        android:lable="@string/app_name"

                                        android:persistent="ture">

                                </application>

                        </manifest>

                如果不希望应用在系统低内存时被系统销毁,需将application标签的android:persistent属性设置为true。

        (2)应用的启动过程

                要启动一个应用,首先要创建一个进程,然后启动UI主线程,接着打开Activity,具体流程见下图所示:

                       

                Android进程分为zygote进程和普通进程。当启动一个应用时,Dalvik虚拟机会先通过NativeStart接口初始化JNI,为通过zygote进程创建新进程做好准备。原生代码的启动位于app_main.cpp中,具体如下:

                        if(0==strcmp("--zygote", arg)){//zygote进程

                                bool startSystemServer=(i<argc)?strcmp(argv[i], "--start-system-server")==0, false;

                                setArgv0(argv0,"zygote");

                                set_process_name("zygote");

                                runtime.start("com.android.internal.os.ZygoteInit", startSystemServer);

                        }else{//普通进程

                                set_Process_name(argv0);

                                runtime.mClassName=arg;

                                runtime.mArgC=argc-i;

                                runtime.mArgV=argv+i;

                                runtime.start();  //调用AndroidRuntime::start()方法

                        }

                在AndroidRuntime.cpp中,需要设置环境变量、启动虚拟器等,具体实现如下:

                        void AndroidRuntime::start(const char* className, const bool startSystemServer)

                        {

                                char* slashClassName=NULL;

                                char* cp;

                                JNIEnv* env;

                                blockSigpipe();

                                if(startSystemServer){

                                }

                                const char* rootDir=getenv("ANDROID_ROOT"); //环境变量

                                if(rootDir==NULL){

                                        rootDir="/system";

                                        if(!hasDir("/system")){

                                                goto bail;

                                        }

                                        setenv("ANDROID_ROOT", rootDir, 1);

                                }

                                if(startVm(&mJavaVM, &env)!=0)        //启动虚拟机

                                        goto bail;

                                        if(startReg(env) < 0){        //注册方法

                                                goto bail;

                                        }

                                }

                        }

                所有的普通进程均是通过zygote进程创建出来的,新进程的创建主要是ZygoteInit()中进行的。方法如下:

                        public static void main(String argv[]){

                                try{

                                        VMRuntime.getRuntime().setMinimumHeapSize(5*1024*1024);        //设置最小堆

                                        SamplingProfilerIntegration.start();

                                        registerZygoteSocket();        //注册套接字

                                        EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis());

                                        preloadClasses();        //加载公共实现

                                        preloadResources();        //加载公共资源

                                        EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis());

                                        SamplingProfilerIntegration.writeZygoteSnapshot();

                                        gc();        //启动垃圾回收

                                        if(argv.length!=2){

                                                throw new RuntimeException(argv[0] + USAGE_STRING);

                                        }

                                        if(argv[1].equals("true")){        //如果是系统服务

                                                startSystemServer();        //启动系统服务

                                        else if(!argv[1].equals("false")){        //普通进程

                                                throw new runtimeException(argv[0] + USAGE_STRING);

                                        }

                                        if(ZYGOTE_FORK_MODE){        //Fork进程,默认为false

                                                runForkMode();

                                        }else {        //构建事件循环

                                                runSelectLoopMode();

                                        }

                                        closeServerSocket();

                                }catch (MethodAndArgsCaller caller){

                                        caller.run();        //抛出MethodAndArgsCaller异常

                                }catch(RuntimeException ex){

                                        closeServerSocket();

                                        throw ex;

                                }

                        }

                runSelectLoopMode()方法的本质为启动一个死循环,利用zygoteConnection通过套接字来处理消息。

                当触发MethodAndArgsCaller异常时,系统会通过Method的invoke方法调用invokeNative方法创建出UI主线程Activitythread,以及设置事件循环等。

                当收到LAUNCH_ACTIVITY事件时,ActivityThread的handleMessage方法会处理消息并调用handleLaunchActivity方法的实现。

                在启动Activity的过程中,为了进行JUnit测试,具体Activity的onCreate方法的调用是通过Instrumentation的callActivityOnCreate方法进行的,在应用的启动过程中会不断调用Instrumentation。

 

0 0
原创粉丝点击