Android ART Runtime (2) – dex2oat

篇文章中我们介绍了 Android 4.4 新开发的运行时 ART 项目,其中的一个重要模快是 dex2oat,简单讲就是使用 LLVM 把 dex 文件编译成 oat 文件(Optimized ART?)。下面我们详细研究一下 dex2oat 的功能,以及他是如何被调用的。

一、dex2oat 简介

dex2oat 顾名思义 dex file to oat file,就是在新旧两种运行时文件的转换。他是一个可执行 ELF 文件,adb 连接手机以后也可以直接调用。dex2oat 的源码只有一个文件 /art/dex2oat/dex2oat.cc。dex2oat 有很多参数,并且实用方法打印到 logcat里面,看起来非常不便,我们直接看源码 Usage()。我挑了几个比较重要的参数看了一下,先介绍一下,调用过程中可能会用到。

dex2oat 用法

  • --dex-file=: specifies a .dex file to compile.: 需要转换的 dex 文件,也可以是 apk, jar,dex2oat 会找到里面的 classes.dex 进行转换。
  • --oat-file=: specifies the oat output destination via a filename.: 指定输出 oat 的文件名。
  • --boot-image=: provide the image file for the boot class path.: 系统运行时工具类在 ART 下编译后的文件,他的例子是指向/system/framework/boot.art。但其实 boot.art 不在这个目录下,而是在/data/dalvik-cache/system@[email protected]。后面还会详细介绍这个 boot 文件。
  • --compiler-backend=(Quick|QuickGBC|Portable): select compiler backend": dex2oat 好像只处理了 Quick 和 Portable 两种编译 backend,暂时还不理解有什么区别,待以后继续研究。

其他参数大多都能从他的描述中知道用途,等以后用到的时候再详细看。

oat 文件在哪里?
我们知道选择 ART 作为运行时后,需要重启手机。然后系统会使用 dex2oat 把所有 App 编译成 oat 文件。那么 oat 都存在哪里?我怎么找不到?通过查看代码和开机的 log,发现 oat 文件原来在 /data/dalvik-cache/*@classes.dex。竟然和 odex 文件名字一样!检验一下文件格式:

果然是 ELF 文件。

二、调用过程分析

PackageManagerService 是负责 apk 包管理的服务,包括安装,检查,删除,更新等等所有与 apk 相关的服务。我在源码中研究了一下 dex2oat 详细的调用过程,在下面介绍过程的时候我打算从 PackageManagerService 入手,经过一系列的调用过程才真正执行 dex2oat。

PackageManagerService (PMS)
我们知道 PMS 服务启动之后会监控某些目录(/data/data/ 等)。如果目录多了某个文件,PMS 会调用一些方法对文件进行处理。扫描监控目录的方法是:scanDirLI(),在 PackageManagerService 的构造方法中调用,详见代码 /frameworks/base/services/java/com/android/server/pm/PackageManagerService.java

从源码中可以看到,有四个地方调用 scanDirLI()。再调用之前有一个 AppDirObserver 类引起了我的注意,看一下是怎么对目录进行监控的。依然是在 PackageManagerService 中找到了 AppDirObserver 继承于 FileObserver。FileObserver 有一个 native 方法 startWatching 找到了他的实现 /frameworks/base/core/java/android/os/FileObserver.java

man 一下 inotify_add_watch:

原来 inotify_add_watch() 是监控目录的。

PackageManagerService.scanDirLI()

回到正题,我们从构造方法中找到了 scanDirLI(),现在来看一下 scanDirLI() 都做了什么:

scanDirLI() 便利所有目录下的 apk 文件并调用 scanPackageLI(),我们继续。

PackageManagerService.scanPackageLI()

scanPackageLI() 先对文件的设置进行读取,还包含其他处理。我们跳过直接看

又调用了另外一个重载的 scanPackageLI()。在这个 scanPackageLI() 里面又进行了一系列的包的解析,都与我们的目标无关,直接跳到这里:

在这里又调用 performDexOptLI() 来处理进行 dex 优化,参数就是这个 package。这里已经感觉到有点快到 dex2oat 的调用点了。我们继续看 performDexOptLI()。

PackageManagerService.performDexOptLI()

performDexOptLI 先检查 dex 是否需要优化,然后再有一些判断,忽略他,看到这里:

最后 mInstaller.dexopt 调用的优化 dex 的函数,Google 在这里把 dex2oat 和 dex2odex 统称为 dexopt。mInstaller 是 Installer 类。下面我们详细了解一下 Installer 的运行机制。

Installer

从 Installer 的代码中我们可以看到 connect(), disconnect(), readBytes(), writeCommand() 等方法。在 connect() 方法中可以看到,Installer 就是与 installd daemon 进行通信的辅助类。代码在这里:

了解了这个之后,我们找一下 dexopt 看看 Installer 向 installd 发送了什么信息:

其实发送了 dexopt apkpath uid 1|0 给 installd。好了,Installer 分析结束,我们转战 installd。

installd.c

installd 是 socket 通信的 server,会 accept 连接,并读取数据。从下面的两个死循环就能看出来:

之后会调用 execute 函数:

execute() 函数先把 cmd 分割然后通过 cmds 表查询函数名称:

dexopt 对应的是 do_dexopt() 函数,do_dexopt() 又调用了 commands.c 里面的 dexopt() 函数。感觉离我们的目标不远了,继续看 commands.c。

commands.c

在 commands.c 中的 dexopt() 函数,我们看到此函数从 property 中获得 dalvik.vm.dexopt-flags 和 persist.sys.dalvik.vm.lib:

dalvik.vm.dexopt-flags 暂时不知道是什么意思,persist.sys.dalvik.vm.lib 就是当前系统使用的运行时默认是 Dalvik VM,如果选择了 ART,应该就是 libart.so 了。

之后函数通过检查是否有 odex 文件来判断是否已经进行过 dexopt。然后进行一系列检查 chmod 和 chown。最后函数 fork 了一个子进程处理 dexopt:

开始先 drop capabilities(关于 Linux capabilities: man capabilities)。最后比较 persist_sys_dalvik_vm_lib,如果是 libart 则调用 run_dex2oat()。Good,终于有头绪了,再看 run_dex2oat():

终于找到了,这就是调用 dex2oat 的地方,写的非常清楚。–zip-fd 是 apk 的 fd,–oat-fd 是输出 oat 的 fd。最终使用 execl() 执行 /system/bin/dex2oat 程序对 apk 进行编译。

三、总结

dex2oat 对所有 apk 进行编译并保存在 dalvik-cache 目录里。PMS 会持续扫描安装目录,如果有新的 App 安装则马上调用 dex2oat 进行编译。

最后,之前我们提到的 boot.art 我在 logcat 中找到了他的来源:

似乎是把所有 framework 的 jar 编译成 boot.art,在 dex2oat 的时候使用。还是有很多不理解的地方,相信在以后的分析中应该有更深入的理解。

附:相关代码链接

  • PackageManagerService constructor: http://androidxref.com/4.4_r1/xref/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java#1062
  • scanDirLI(): http://androidxref.com/4.4_r1/xref/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java#3445
  • scanPackageLI(): http://androidxref.com/4.4_r1/xref/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java#4103
  • performDexOptLI(): http://androidxref.com/4.4_r1/xref/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java#3860
  • Installer.dexopt(): http://androidxref.com/4.4_r1/xref/frameworks/base/services/java/com/android/server/pm/Installer.java#204
  • do_dexopt(): http://androidxref.com/4.4_r1/xref/frameworks/native/cmds/installd/installd.c#37
  • dexopt(): http://androidxref.com/4.4_r1/xref/frameworks/native/cmds/installd/commands.c#653
  • run_dex2oat(): http://androidxref.com/4.4_r1/xref/frameworks/native/cmds/installd/commands.c#run_dex2oat
This entry was posted in Android, Technology and tagged Android, ART Runtime, dex2oat on January 10, 2014 by Mingshen Sun.

你可能感兴趣的:(Android ART Runtime (2) – dex2oat)