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

Android PreLink 技术简介

2011-09-09 10:14 239 查看
1.      原理简介

1)          Prelink

Prelink 即预链接技术是利用事先链接以代替运行时链接的技术,以加快共享库的加载速度,它不仅能加快程序启动时间,还可以减少部分内存开销(它能使 KDE 的启动时间减少 50% )。每次程序执行时,进行的链接动作都是一样的,链接相对来说开销很大,尤其是嵌入式系统。

2)          普通 Linux 系统的 Prelink

Redhat 系统中 prelink 工具 (/etc/cron.dialy/prelink) 会修改可执行程序,把它与所需库的链接信息加入可执行程序。在程序运行时,使用 glibc(glibc
> 2.3.1-r2) 中的 ld-linux.so 来进行链接。用此方式,每次更新动态库后,使用它的程序都需要重新 prelink ,因为新库中的符号信息,地址等很可能与原来不同了。

3)          Android 的 Prelink

Android 源码中有一组 map 文件,其中定义了需要预连接的动态库,其 Prelink 信息以及对应的逻辑地址( 4G 地址空间中位置),在动态库编译时,预处理程序 apriori 根据 map 文件中的定义,生成预链接信息重定向信息,并加入这些二进制文件 lib*.so 的末尾。它主要节约了查询函数地址等工作所用的时间,动态库重定位的开销。在运行程序,动态库加载时,加载程序 linker 判断动态库是否为 Prelink 的,如果是的话,就在首次使用时将其加载到指定的内存空间,直接使用预编译信息。

2.      源码分析

1)          动态库的编译脚本

a)           源代码 
frameworks/base/media/libmedia/Android.mk 等库的编译选项文件

b)          配置 
可在 Android.mk 中设置该库是否需要 Prelink ,默认是使用 Prelink 的,也可设置成否,方法如下: 
LOCAL_PRELINK_MODULE := false

c)           分析 
此设置只用于动态库的编译,编译时用 showcommands 参数即可看到具体编译使用到的命令行,如在某个库的目录中运行 mm
showcommands ,即可看到相应的 Prelink 操作,示例如下: 
target Prelink: libxxx

out/host/linux-x86/bin/apriori --prelinkmap build/core/prelink-linux-arm-2G.map --locals-only --quiet xxx.so --output xxx.so
如果该库设置为需要 prelink ,则也需要在 map 文件中加入相应项,否则编译不通过

2)          内核

a)           源码 
kernel/arch/arm/configs/xxx_defconfig

arch/arm/mach-msm/include/mach/vmalloc.h

b)          配置 
CONFIG_VMSPLIT_2G=y    一般默认为 3G /1G ,此项 即设置为 2G /2G

c)           分析 
xxx_defconfig 为默认的内核配置文件 ( 修改其中的 CONFIG_VMSPLIT_*) ,也可通过 make
menuconfig 配置,与 Prelink 相关的主要是指定用户空间和内核空间内存如何分配 4G 的虚拟内存空间( Memory
split ),一般有三种方式: 3G /1G , 2G /2G ,1G /3G(user/kernel) ,一般默认的是用户空间 3G (0x0-0xBFFFFFFF) ,内核空间 1G (0xC0000000
- 0xFFFFFFFF)

d)          内存分析示例,以 3G /1G 为例 ( 见 build/core/prelink-linux-arm.map)

0xC0000000 - 0xFFFFFFFF Kernel

0xB0100000 - 0xBFFFFFFF Thread 0 static

0xB0000000 - 0xB00FFFFF Linker

0xA0000000 - 0xBFFFFFFF Prelinked System Libraries

0x90000000 - 0x9FFFFFFF Prelinked App Libraries

0x80000000 - 0x8FFFFFFF Non-prelinked Libraries

0x40000000 - 0x7FFFFFFF mmap’s stuff

0x10000000 - 0x3FFFFFFF Thread Stacks

0x00000000 - 0x0FFFFFFF .text / .data / heap

3)          map 文件

a)           源代码 
build/core/prelink-linux-<arch>*.map

b)          配置 
编译时有多个 map 文件可先,根据不同的硬件平台及内存分配 (3G/1G,
2G/2G) 修改系统配置 deviceBoardConfig.mk 中设置 
TARGET_USES_2G_VM_SPLIT := true  以配置 Prelink 的地址空间

c)           分析 
apriori 中的 prelinkmap.c 它用根据整个系统设置 device/*/BoardConfig.mk 的内存分配规则( 3G /1G,
2G/2G )来判断 map 中指定地址是否符合 Prelink 的地址空间范围,如果正常,则在 so 的末尾加入 prelink 信息和标识 ( 文件以 PRE 结束 )
apriori 可以预先为若干共享库确定加载地址,并为有依赖关系的共享库做静态重定位和连接 ,  该命令加入参数 --verbose ,即可显示出 prelink 的细节。

5)          linker 程序

a)           源代码 
bionic/linker/*

(bionic 目录中存放一些基础的库,如 libc,
libstdc++, libthread_db, linker 等 )

b)          分析 
linker 是 Android 的专用动态链接库键接器, Linker 和传统 Linux 使用的 linker(ld.so,ld-linux.so.2,ld-linux.so.3) 有所不同。库的编译参数 -dynamic-linker 指定了键接器为 /system/bin/linker( 也可以手动换成别的 ) , 该信息将被存放在 ELF 文件的 .interp 节中,内核执行目标映像文件前将通过该信息加载并运行相应的解释器程序 linker ,并链接相应的共享库, 共享库以 ELF 文件的形式保存在文件系统中 。 核心的 load_elf_binary 会首先将其映像文件映射到内存,然后映射并执行其解释器也就是 linker 的代码。 linker 的代码段是进程间共享的,但数据段为各进程私有。 
所有外部过程引用都在映像执行之前解析 ,  Android 中的共享库和可执行映像都默认采用 ELF 格式的文件 .  程序头表包含了加载到内存中的各种段的索引及属性信息,它将告诉加载器如何加载映像,初始化时,动态链接器首先解析出外部过程引用的绝对地址,一次性的修改所有相应的 GOT 表项。 
linker 会在共享库加载时,调用 is_prelinked 查看该库是否是 prelink 的,并在 alloc_mem_region 中检查目的地址是否被占用。 如果该库不是 prelink 的,则库加载的起始地址为零。

3.      参考文档:

1)          动态库优化—— Prelink (预连接)技术 
http://www.eefocus.com/article/09-04/71629s.html

2)          Android
build system note -  一醉千年  -
CSDN 博客 
http://blog.csdn.net/yili_xie/archive/2009/12/01/4906865.aspx

3)          Linux,
Android 基础知识总结 
http://wenku.baidu.com/view/f68fc029647d27284b735100.html

4)          android
Linker 浅析 
http://blog.csdn.net/dinuliang/archive/2010/04/20/5509009.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息