Android系统编译原理

来源:互联网 发布:300451创业软件新闻 编辑:程序博客网 时间:2024/04/30 18:56
[1] 历史
    2003年Android公司成立,系统开发
    2005年Android被google收购
    2007年11月5日,google公司推动成立手机开发联盟(HAL)
    2008年9月Android 1.0正式发布,HTC G1使用
    
[2] 如何研究学习系统?
    1. 了解系统
       (1) 架构
       (2) 内核
       (3) 文件系统
       操作系统 = 内核 + 文件系统(数据和程序分类存储)
       
    2. 使用系统
       运行
       
    3. 研究学习系统原理
       (1) 获取源码
       (2) 编译运行
       (3) 编译原理
       (4) 启动过程
       
       (5) 定制系统
       
[3] 概述
    理想的手机操作系统 = 应用通用 + 容易开发友好的应用 + 容易开发手机应用
    
    应用层
    ---------------------------------------
    如何让手机应用容易开发?
    如何让友好的应用容易?
    应用框架层
    ---------------------------------------
                             如何让应用通用?
                                  vm
    系统运行库 和 系统服务
    ---------------------------------------
    如何让硬件更容易使用?
    linux内核
    ---------------------------------------
    hardware


[4] 应用层
    1. Android 系统中所有应用程序的地位平等, 系统不绑定--开放
    2. 应用层是应用程序开发工程师工作的层次
    
[5] 应用框架层(Application Framework)
    如何让友好的应用容易?
    1. Activity Manager
       管理Activity之间的切换
       
    2. Window Manager
       管理窗口之间的切换
       
       Activity =  Window + 用户交互代码
       
    3. Content Provider
       程序<--------------Content Provider------------>程序
       例:
       phone                                           联系人
       sms
       
    4. View System
       基本界面组件实现
       
    5. Package Manager
       管理应用程序包
       
    6. Resource Manager
       资源管理, 资源包含: 字符串、图片和布局
       
    7. XMPP(Extensible Messging and Presence Protocol) Service
       可扩展消息与表示协议, 四大即时通讯协议协议,基于xml脚本实现。
       Google的Gtalk基于基于协议协议。
    
    如何让手机应用容易开发?
    1. Notification Manager
       通知管理
       
    2. telephone Manager
       电话管理


    3. Location Manager
       定位管理,可以获得当前的位置信息
       
注意: 蓝颜色的部分用java语言实现
       
[6] 虚拟机
    1. Core Libaries
       Java语言核心库
       
    2. Dalvik(冰岛小渔村的名字, 非常小,非美丽)Virtual Machine
       (1) 每个应用程序运行在自己独立的虚拟机上, 每个虚拟机一个进程
       (2) 基于寄存器(指令支持的操作数只能是寄存器和立即数)实现
       (3) 执行.dex文件(针对内存做了优化)
       (4) java类--->.class文件---dx(SDK)---->.dex文件
       (5) 依赖于2.6以上版本的内核, 因为在2.6以上版本的内核中,加入一些虚拟机需要的机制:
           线程和底层的内存管理机制
           
[7] 系统运行库
    1. libc
       标准C库
       
    2. SSL(Secure Socket Layer 安全套接层)
       在网络传输时, 加入对数据的加密, 有以下三个功能:
       (1) 使用公钥证书对双端进行认证
       (2) 通讯加密
       (3) 数据完整性检查
       
    3. SGL
       2D图形加速引擎
       
    4. Webkit
       web浏览器引擎,支持了Android和一个内嵌web视图
       
    5. FreeType
       位图和适量字体图库(字库)
       
    6. OpenGL | ES
       3D图形加速引擎
       
    7. SQLite
       开源小型关系型数据库
       
    8. Media Framework
       基于PacketVideo Open Core实现, 支持很多的多媒体格式,音频(mp3, AAC和AMR)、视频(mpeg4、H.264 ...)
       支持图片文件(jpg、png...)
       
    9. Surface Manager
       对显示子系统进行管理,用于应用程序的2D和3D图形融合
       
   注意: 绿颜色部分用C/C++实现
      
[8] kernel
    Binder 用于android系统中进程间通信
   
[9] 文件系统
    1. /d
       链接到/sys/kernel/debug
       
    2. /data
       用户数据
    
    3. /dev
       设备文件
       
    4. /etc
       链接到/system/etc
       
    5. /mnt
       外部文件系统的挂载点, 如: SDcard
       
    6. /proc 和 /sys
       挂载procfs和sysfs虚拟文件系统
       
    7. /root
       root用户的home目录
       
    8. /sbin
       基本调试工具和启动程序
       
    9. /sdcard
       链接到/mnt/sdcard目录
       
    10. /system
       * /system/app            apk应用程序
       * /system/preinstall
         /system/pri-app
       
       
       * /system/bin            linux应用程序(常用工具)
       * /system/etc            启动脚本和配置文件
       * /system/fonts          字库
       * /system/framework      Application Framework编译出来的包
       * /system/lib            linux的动态库
         /system/vendor/lib     厂家动态库
       
       
       /system/media          开机音乐和系统logo
       
       * /system/vendor/modules linux驱动模块
       
       /system/xbin          linux应用程序
       
       /vendor
       链接到/system/vendor
       
[10] 获取源码及编译环境配置
     1. Android官网
        (1) 下载源码(repo 和 git)
            断电续传的脚本:
            #!/bin/sh
            
            repo sync
            while [ $? -ne 0 ]
            do
            repo sync
            done
            
        (2) 配置编译环境
            《开源平板编译环境配置.docx》
            
     2. 芯片厂家(芯片代理商/开发板厂家)
     3. SOC开源社区
     
[10] Android源代码目录
     dalvik             虚拟机相关工具
     *device(vendor)    厂家目录
     ndk                开发本地工具箱代码
     
     *system             系统核心程序和本地服务程序的源码
     system/core        系统核心程序源码
     system/media       多媒体应用程序支持的源代码
     
     *build              编译脚本(Makefile shell(bash))
     
     *development        开发工具和例子程序源码
     *developers         开发工具和例子程序源码
     
     *frameworks         应用框架层源码
     
     *external           第三方的开源库源代码
     *hardware           硬件抽象层代码
     *packages           应用程序及包的源代码
     sdk                应用程序开发工具箱中工具源代码
     
     *out               编译结果
     
[11] 配置编译
     u-boot & kernel
     ---------------
     $ cd lichee
     $ ./build.sh config    第一次编译
     $ ./build.sh
     
     Android
     ----------------
     $ cd android 4.4
     
     1. source build/envsetup.sh
        (1) 功能
            1. 添加配置编译命令到当前shell进程
               hmm(help)           打印配置编译命令的帮助信息
               tapas               配置编译应用程序(full模拟器)
               例:
                  $ tapas packages/apps/Contacts
                  
               croot               在任意的android源代码目录下,回到android源代码根目录
               m/mm/mmm/mma/mmma   编译命令
               cgrep               从C/C++文件中搜索特征字符串
               例:
                  $ cgrep RILD
                  
               jgrep               从java文件中搜索特征字符串
               resgrep             从资源文件中搜索特征字符串
               godir               进入具有指定文件的目录
               例:
                  $ godir rild.c
                  
               printenv            打印配置结果
               
            2. 添加"产品型号-编译类型"到选择菜单(shell进程)
               产品型号
               full                android模拟器
               vbox_x86            x86的android虚拟机
               fspad_723
               
               编译类型
               eng                 工程机(包含开发工具程序)
               userdebug           用户调试机(包含部分调试程序)
               user                普通用户机
               
        (2) 结果
            命令可以直接shell运行:
            $ hmm
        
     2. lunch(配置)
        (1) 功能
            1. 选择产品型号和编译类型
               "产品型号-编译类型"
               
            2. 检查产品是否存在, 存在, 获取产品信息, 根据产品信息判断设备是否存在,存在找到设备,获取设备信息(选择跟硬件相关的软件模块)
            3. 检查选择的编译类型(选择软件模块)是否正确
            4. 打印选择产品信息及其设备信息
            
        (2) 结果(lunch的结果)
            PLATFORM_VERSION_CODENAME=REL               代码名称, REL代表发布版本, 除Android系统开发团队外,其他看到的都是REL
            PLATFORM_VERSION=4.4.2                      Android版本号
           *TARGET_PRODUCT=fspad_723                    产品型号
           *TARGET_BUILD_VARIANT=eng                    编译类型
           *TARGET_BUILD_TYPE=release                   编译类型(release 和 debug), 可以在Android.mk中使用
            TARGET_BUILD_APPS=                          指定当前编译的应用模块
                                                        空                    编译整个Android系统
                                                        应用程序(packages)    编译应用程序(full为目标机)
            TARGET_ARCH=arm                             CPU架构
            TARGET_ARCH_VARIANT=armv7-a-neon            指令集版本 neon 浮点协处理器
            TARGET_CPU_VARIANT=cortex-a7                CPU名
            HOST_ARCH=x86                               编译Android系统的主机类型
            HOST_OS=linux                               编译Android系统的操作系统名
            HOST_OS_EXTRA=Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty
                                                        操作系统名全称
            HOST_BUILD_TYPE=release                     SDK工具为release
            BUILD_ID=KVT49L                             Android版本名
            OUT_DIR=out                                 编译结果的输出目录
        
     2' extract-bsp(平板特有的,非标准文件)
        拷贝kernel 和 驱动模块到Android目录下
        
     3. make(编译)
        (1) 功能
            1. 获取产品信息, 根据产品信息,获取设备信息, 根据设备信息选择源代码模块
            2. 根据编译类型,选择产品中需要的所有的软件的源代码
            3. 编译源代码
            
        (2) 扩展
           *make -j2               启动2(x86 CPU是单核)个线程(job)编译系统
            make help              打印帮助信息
           *make clean             清除编译出来的所有文件
           *make snod              重新生成system.img
           *make ramdisk           重新生成ramdisk.img
           *make bootimage         重新生成boot.image
            make recoveryimage     重新生成recovery.img
            
            make 模块名            编译指定模块
            例: $ make rild
            
           *make clean-模块名      清除指定模块
            例: $ make clean-rild
            
            m                      编译整个android系统,等价于make
           *mm                     编译当前目录及其子目录下的所有模块,不编译依赖模块
            例:
               $ cd hardware/ril/rild
               $ mm
               
            *mmm                    编译指定目录及其子目录下的所有模块, 不编译依赖模块
            例:
               $ mmm hardware/ril/rild
               
            mma                    编译当前目录及其子目录下的所有模块,编译依赖模块
            mmma                   编译指定目录及其子目录下的所有模块, 编译依赖模块
            
       
     4. pack
        bootloader.fex + boot.img(kernel + ramdisk.img) + system.img + userdata.img + recorvery.img = lichee/tools/pack/sun8iw3p1_android_fspad-723_card0.img

        bootloader.fex = BOOT0 + u-boot.bin + logo


[1] source build/envsetup.sh
    1. 功能
       (1) 添加配置编译命令到shell进程
       (2) 添加"产品型号-编译类型"菜单到shell进程
       
    2. 原理
       (1) /bin/bash脚本的执行原理
           1. 语句
              执行
              if [ $PATH ]; then
              ...
              fi
              
           2. 命令或脚本
              1. fork进程
              2. exec(普通程序或脚本, 参数, 环境变量)
              
              echo "hello"
              
           3. 变量赋值
              添加"变量=值"到当前shell进程的环境变量区
              VAR="hello"
              
           4. 函数定义
              添加函数定义到当前shell进程的环境变量区
              func()
              {
              ...
              }
              
              函数什么执行?
              func
              
     3. 总结
        (1) 添加配置编译函数到shell进程
        (2) 添加"产品型号-编译类型"菜单到shell进程的LUNCH_MENU_CHOICES环境变量数组
            $ set | grep LUNCH_MENU_CHOICES
            
[2] lunch
    1. 功能
       (1) 选择产品型号和编译类型
           "产品型号-编译类型"
           
       (2) 检查产品是否存在, 存在, 获得产品信息, 根据产品信息获得设备信息,
           检查设备是否存在, 存在, 获得设备信息
           
       (3) 检查选择的编译类型是否正确, 正确,利用这个信息,选择软件(跟调试有关的)模块
       (4) 打印产品信息、设备信息和选择结果(当前shell进程的环境变量区)
       注意: 选择结果存放在当前shell进程的环境变量区
       
    2. 原理
       (1) 重要的文件(厂家目录结构图.bmp)
           vendorsetup.sh        添加"产品型号-编译类型"到lunch菜单
           AndroidProducts.mk    产品列表文件
           产品名.mk             产品信息文件
           BoardConfig.mk        设备信息文件
           
       (2) 选择产品型号和编译类型
           通过shell脚本,显示菜单,并且让用选择,选择的结果存放在当前shell进程的环境变量区
           
       (3) 检查产品是否存在, 存在, 获得产品信息, 根据产品信息获得设备信息
           检查设备是否存在, 存在, 获得设备信息
           1. 获取产品列表文件
              find device -maxdepth 6 -name AndroidProducts.mk     当前版本
              find vendor -maxdepth 6 -name AndroidProducts.mk     以前版本
              
              build/target/product/AndroidProducts.mk              系统自带的默认产品
              
           2. 获取产品信息文件
               在产品列表文件中, PRODUCT_MAKEFILES变量中存放了产品信息文件:
               PRODUCT_MAKEFILES := \                                                                              
                $(LOCAL_DIR)/fspad_723.mk \
                ...
              
           3. 找到选择产品的产品信息文件
              用TARGET_PRODUCT中存放的产品名(选择的产品的名称)和整个系统中的产品信息文件中的产品名(存放在PRODUCT_NAME变量)
              逐一对比, 如果找到相等的,找到产品信息文件,如果找不到相等的,则产品不存在
              
           4. 获取设备名
              产品信息文件中的PRODUCT_DEVICE变量中,就是存放的设备名
              
           5. 获取设备信息文件
              PRODUCT_DEVICE----传值---->TARGET_DEVICE
              
              device/*/$(TARGET_DEVICE)/BoardConfig.mk                  现在版本的厂家目录
              vendor/*/*/$(TARGET_DEVICE)/BoardConfig.mk                以前版本的厂家目录
              
              build/target/product/$(TARGET_DEVICE)/BoardConfig.mk
      
      (4) 检查选择的编译类型是否正确, 正确,利用这个信息,选择软件(跟调试有关的)模块
          lunch函数完成
          
      (5) 打印产品信息、设备信息和选择结果(当前shell进程的环境变量区)
          printconfig
          
[3] make(make.pdf)
    1. 功能
       (1) 根据产品型号, 找到产品信息,获得设备名,根据设备名,找到设备信息
       (2) 根据设备信息,选择硬件相关的软件模块
       (3) 根据编译类型, 选择调试相关的软件模块
       (4) 编译选取的软件模块
       
    2. 原理
       (1) 根据产品型号, 找到产品信息,获得设备名,根据设备名,找到设备信息
           build/core/confg.mk
           
       (2) 根据设备信息,选择硬件相关的软件模块
           根据BoardConfig.mk中的信息,选取软件模块, 如:
           如果选取了wifi,wifi依赖的应用程序、库、驱动模块等都会被编译到系统
           
       (3) 根据编译类型, 选择调试相关的软件模块
           产品有以下三种编译类型:
           eng                工程机版本,开发阶段使用,有大量的调试程序
           userdebug          用户调试机, 测试阶段使用, 有root权限,含调试测试程序
           user               用户机, 最终用户使用
           
           每个模块都存在标签:
           eng                工程机使用的模块
           debug              用于用户调试机的模块
           tests              测试模块
           samples            样例
           optional           自由选择模块
           空                 没有标签
           
           如何根据编译类型选择模块?
           eng                编译标签为eng和debug的模块
                              产品模块(PRODUCT_PACKAGES)
                              
           userdebug          编译标签为debug的模块
                              产品模块(PRODUCT_PACKAGES)
                              
           user               产品模块(PRODUCT_PACKAGES)
           
       (4) 编译总框架
           build/core/Makefile(主要的编译规则)
           build/core/main.mk(主要的Makefile)
           
           
       (5) $(ONE_SHOT_MAKEFILE)
           编译整个工程, 此变量为空
           编译模块时,ONE_SHOT_MAKEFILE装载编译模块的Makefile文件, 文件名叫Android.mk
           
           1. 如何管理模块?
              (1) 一个模块一个Makefile文件(名称: Android.mk)
              (2) 每个模块编译出来一个程序、库、java包
              
           2. 如何添加一个模块到Android系统?
              (1) 添加程序或库或包的源代码
              (2) 添加Android.mk
              
           3. 如何编写Android.mk?
              《Android.mk说明》
              
           
         
[4] 总结
    1. 如何添加产品?(所有的源代码及配置文件使用fspad_723)
       (1) 添加vendorsetup.sh文件
       (2) 添加AndroidProducts.mk文件
       (3) 添加T3.mk(拷贝参考产品)
           PRODUCT_NAME
           PRODUCT_DEVICE =   
       (4) 添加BoardConfig.mk(拷贝参考产品)
       
    2. 如何添加一个模块?(apk)


Android.mk说明:

1. 设置当前模块的编译路径为当前文件夹路径
   LOCAL_PATH := $(call my-dir)


2. 清理(可能由其他模块设置过的)编译环境中用到的变量
   include $(CLEAR_VARS)


3. 模块编译变量
   变量                          用途
   LOCAL_SRC_FILES               当前模块包含的所有源代码文件
   LOCAL_MODULE                  当前模块的名称,这个名称应当是唯一的,模块间的依赖关系就是通过这个名称来引用的
   LOCAL_C_INCLUDES              C/C++ 语言需要的头文件的路径
   LOCAL_STATIC_LIBRARIES        当前模块在静态编译时,需要的静态库
   LOCAL_SHARED_LIBRARIES        当前模块在运行时依赖的动态库
   LOCAL_CFLAGS                  C/C++编译器的参数
   
   include $(BUILD_EXECUTABLE)
   gcc $(LOCAL_CFLAGS) $(LOCAL_SRC_FILES) -o $(LOCAL_MODULE) -I$(LOCAL_C_INCLUDES) $(LOCAL_STATIC_LIBRARIES) -l$(LOCAL_SHARED_LIBRARIES) 
   
   LOCAL_JAVA_LIBRARIES          当前模块依赖的Java共享库
   LOCAL_STATIC_JAVA_LIBRARIES   当前模块依赖的Java静态库
   LOCAL_PACKAGE_NAME            当前模块的APK应用的名称
   
   LOCAL_CERTIFICATE             签署当前应用的证书名称
   
   LOCAL_MODULE_TAGS             当前模块所包含的标签,Android.mk的必选,一个模块可以包含多个标签
                                 标签的值可能是debug, eng, tests, samples 或 optional
                                 
   build/core/definitions.mk
   通常会用下面函数获取上面环境变量的值:
   提供配置编译需要的函数
   $(call my-dir)                        获取当前文件夹路径
   $(call all-subdir-java-files)         获取当前目录子目录下所有的java源代码文件
   $(call all-java-files-under, 目录)    获取指定目录下的所有Java文件
   $(call all-c-files-under, 目录)       获取指定目录下的所有C语言文件
   $(call all-Iaidl-files-under, 目录)   获取指定目录下的所有 AIDL 文件
   $(call all-makefiles-under, 目录)     获取指定目录下的所有Make文件
 
4. 模块类型(make.pdf)
   include $(BUILD_%_%)          %代码表0个或多个字符


   BUILD_EXECUTABLE              编译目标机上的可执行文件(ELF)
   BUILD_STATIC_LIBRARY          编译目标机上的静态库(*.a 编译时使用)
   BUILD_SHARED_LIBRARY          编译目标机上的动态库文件(*.so)
   BUILD_JAVA_LIBRARY            编译目标机上的java动态库
   BUILD_STATIC_JAVA_LIBRARY     编译目标机上的java静态库
   BUILD_PACKAGE                 编译目标机上的java包
   
   寻找Android.mk例子的方法:
   find . -depth -name Android.mk -exec grep BUILD_STATIC_LIBRARY {} \;


产品信息文件说明:

在产品配置文件中, 存储产品信息,这些信息存放在变量中:
变量                           说明
PRODUCT_COPY_FILES             编译该产品时需要拷贝的文件,以“源路径 : 目标路径”的形式
PRODUCT_PROPERTY_OVERRIDES     产品属性, 最终放入/system/build.prop
PRODUCT_PACKAGE_OVERLAYS     不修改packages中apk的情况下,自定义产品中的framework和package中的资源文件
DEVICE_PACKAGE_OVERLAYS        不修改packages中apk的情况下,自定义设备中的framework和package中的资源文件


PRODUCT_BRAND                  产品商标
* PRODUCT_NAME               最终用户将看到的完整产品名,会出现在“关于手机”信息中
* PRODUCT_DEVICE               产品设备名
PRODUCT_MODEL                  产品型号,最终用户将看到


PRODUCT_CHARACTERISTICS        tablet-平板模式 phone-电话模式
PRODUCT_AAPT_CONFIG            指定支持哪些尺寸,哪些密度(dot per inch)的屏幕           
PRODUCT_AAPT_PREF_CONFIG       指定屏幕尺寸及密度
                               android系统根据屏幕尺寸和密度来加载相应图片资源,使得android界面可以适用各种界面
                               具体见《Android屏幕规范.jpg》


PRODUCT_LOCALES               产品支持的地区,以空格分格
*PRODUCT_PACKAGES             产品版本中包含的应用程序, 以空格分格
PRODUCT_MANUFACTURER           产品厂家
PRODUCT_OTA_PUBLIC_KEYS        对于该产品的OTA公开key列表
PRODUCT_POLICY                 产品使用的策略
PRODUCT_CONTRIBUTORS_FILE      HTML文件, 包含项目的贡献者
PRODUCT_TAGS                 产品标签,以空格分格


Android文件系统:



编译结果:

out
├── host                               SDK 中的各种工具(emulator,adb...)
│   ├── common                         所有主机都用的通用库(java库)
│   │   └── obj
│   │       └── JAVA_LIBRARIES
│   └── linux-x86                      用linux-x86主机上工具程序
└── target                             目标机上运行的各种程序
    ├── common                         所有产品都用的通用程序(java程序或库)
    │   ├── docs                       文档目录
    │   ├── obj                        中间文件
    │   │   ├── APPS                   应用程序的中间文件
    │   │   └── JAVA_LIBRARIES         java库的中间文件
    │   └── R                          资源文件(java代码形式)
    │       ├── android
    │       ├── com
    │       ├── jp
    │       └── org
    └── product                         产品(特定平台)专用程序
        └── fspad-723                   fspad-723型号产品(全志A23平台)程序
            ├── data                    用户数据, 该目录中的内容被挂载到/data目录下
            ├── obj
            ├── recovery                恢复版的根文件系统
            ├── root                    根文件系统,装有最基本的命令, 该目录中的内容被挂载到/目录下
            ├── symbols
            ├── system                  系统文件系统, 该目录中的内容被挂载到/system目录下
            -------------------------------------------------------------
            ├── ramdisk.img             root目录打包
            ├── ramdisk-recovery.img    recovery目录打包
            ├── boot.img                kernel + ramdisk.img
            ├── system.img              system目录打包
            ├── userdata.img            data目录打包
            └── recovery.img            kernel + ramdisk-recovery.img
            
     为什么root目录打包后叫ramdisk.img?
     (1) 什么是ramdisk?
         用内存模拟磁盘存放文件系统, 内存称为ramdisk
         
     (2) 为什么root目录打包后叫ramdisk.img?
         root目录下的内容在运行时存放在内存
         注意: root目录下存放的是根文件系统
     
     为什么kernel + ramdisk.img被打包成boot.img?
     android系统将kernel和根文件系统存放在一个分区,便于android系统启动时,一起拷贝
     到内存,内核启动完成后,直接挂载根文件系统

1 0
原创粉丝点击