一、映射OAT文件到内存并解析头部

若backend是Quick类型,那么会生成quick类型的机器指令。Quick类型的机器指令的函数依赖关系由JNI函数表描述,而不需要通过动态链接器进行重定位。
通过OatFile类的静态成员函数OpenElfFile来手动加载指定的OAT文件。这种方式是按照ELF文件格式来解析要加载的OAT文件的。
由ElfFile::Open()将oat文件的elf头和program headers通过 mmap() 映射到虚拟内存空间。由OatFile::Setup函数解析这些头部信息,并将获得的信息存入对应的对象和成员变量中,主要是OatDexFile对象。
begin_和end_这两个值比较关键,是Setup函数的主要依据。begin_指向的是映射到虚拟内存中的oatdata段(即OAT头)的起始地址,这个OAT头包括OAT实际内容的所有头部信息和DEX文件内容。end_则是虚拟内存中oatexec段的结束地址,在解析OAT头时用来验证数据的正确性。oatdata段的内容如下所示:

重点展示一下图中第3步,加载elf头和program headers

注:调用mmap()的时候仅仅申请一个vm_area_struct来建立
文件与虚拟内存的映射,并没有建立虚拟内存与物理内存的映射。Linux并不在调用mmap()时就为进程分配物理内存空间,直到下次真正访问地址空间时发现数据不存在于物理内存空间时,触发Page Fault即缺页中断,Linux才会将缺失的Page换入内存空间.
mmap()仅分配一个虚拟内存区域,并不分配物理内存区域。根据文件系统类型,将vma->vm_ops设为对应的file_operations. 这里有个很关键的结构体const struct file_operations *f_op;它是文件驱动操作的入口,在open的时候,完成file_operations的绑定,open流程跟mmap类似。
二、查找类方法的所有本地机器指令

Android系统将ART运行时抽象成一个Java虚拟机。JavaVM接口,与JNIEnv接口的FindClass函数通过 classname 去加载该类所有机器指令,返回到一个OatClass对象中。并且通过LinkCode为类中的每个类方法设置好 解释器入口点 。
然后GetStaticMethodID函数通过 名字和签名 去这个Class对象中查找对应的类方法,返回一个jMethodID值,它就是一个ArtMethod对象指针。ArtMethod对象里保存着机器指令。
为什么每个类方法需要一个解释器入口点?
在ART虚拟机中,并不是所有的类方法都是有对应的本地机器指令的,并且即使一个类方法有对应的本地机器指令,当ART虚拟机以解释模式运行时,它也需要通过解释器来执行。当以解释器执行的类方法在执行的过程中调用了其它的类方法时,解释器就需要进一步知道被调用的类方法是应用以解释方式执行,还是本地机器指令方法执行。为了能够进行统一处理,就给每一个类方法都设置一个解释器入口点。
抽象方法声明类中是没有实现的,必须要由子类实现。因此抽象方法在声明类中是没有对应的本地机器指令的,它们必须要通过解释器来执行。
三、执行类方法的过程分析

找到类方法对应的ArtMethod对象后,通过CallStaticVoidMethod函数去执行。先创建一个ManagedStack类型的调用栈帧,然后获取类方法的解释器入口点,之后通过一个stub(汇编代码)去间接调用该入口点。
本文深入解析Android ART运行时的工作原理,包括OAT文件的加载与解析过程,类方法本地机器指令的查找,以及类方法执行的具体步骤。揭示了ART如何通过OAT文件生成本地机器指令,以及解释器入口点的作用。


513

被折叠的 条评论
为什么被折叠?



