您的位置:首页 > 移动开发 > Android开发

Android系统编译原理

2015-08-13 10:50 330 查看
[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系统启动时,一起拷贝

到内存,内核启动完成后,直接挂载根文件系统
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: