欧美成人午夜免费全部完,亚洲午夜福利精品久久,а√最新版在线天堂,另类亚洲综合区图片小说区,亚洲欧美日韩精品色xxx

Android運(yùn)行時(shí)ART加載OAT文件的過(guò)程分析

2015-05-11 15:14:39 2068瀏覽


    在前面一文中,我們介紹了Android運(yùn)行時(shí)ART,它的核心是OAT文件。OAT文件是一種Android私有ELF文件格式,它不僅包含有從DEX文件翻譯而來(lái)的本地機(jī)器指令,還包含有原來(lái)的DEX文件內(nèi)容。這使得我們無(wú)需重新編譯原有的APK就可以讓它正常地在ART里面運(yùn)行,也就是我們不需要改變?cè)瓉?lái)的APK編程接口。本文我們通過(guò)OAT文件的加載過(guò)程分析OAT文件的結(jié)構(gòu),為后面分析ART的工作原理打基礎(chǔ)。

OAT文件的結(jié)構(gòu)如圖1所示:

    由于OAT文件本質(zhì)上是一個(gè)ELF文件,因此在最外層它具有一般ELF文件的結(jié)構(gòu),例如它有標(biāo)準(zhǔn)的ELF文件頭以及通過(guò)段(Section)來(lái)描述文件內(nèi)容。

     作為Android私有的一種ELF文件,OAT文件包含有兩個(gè)特殊的段oatdataoatexec,前者包含有用來(lái)生成本地機(jī)器指令的dex文件內(nèi)容,后者包含有生成的本地機(jī)器指令,它們之間的關(guān)系通過(guò)儲(chǔ)存在oatdata段前面的oat頭部描述。此外,在OAT文件的dynamic段,導(dǎo)出了三個(gè)符號(hào)oatdataoatexecoatlastword,它們的值就是用來(lái)界定oatdata段和oatexec段的起止位置的。其中,[oatdata, oatexec - 4]描述的是oatdata段的起止位置,而[oatexec, oatlastword]描述的是oatlastword的起止位置。要完全理解OAT的文件格式,除了要理解本文即將要分析的OAT加載過(guò)程之外,還需要掌握接下來(lái)文章分析的類和方法查找過(guò)程。

    在分析OAT文件的加載過(guò)程之前,我們需要簡(jiǎn)單介紹一下OAT是如何產(chǎn)生的。如前面Android ART運(yùn)行時(shí)無(wú)縫替換Dalvik虛擬機(jī)的過(guò)程分析一文所示,APK在安裝的過(guò)程中,會(huì)通過(guò)dex2oat工具生成一個(gè)OAT文件:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,

const char* output_file_name, const char* dexopt_flags)

{

static const char* DEX2OAT_BIN = "/system/bin/dex2oat";

static const int MAX_INT_LEN = 12; // '-'+10dig+'' -OR- 0x+8dig

char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];

char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];

char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];

char oat_location_arg[strlen("--oat-name=") + PKG_PATH_MAX];

sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);

sprintf(zip_location_arg, "--zip-location=%s", input_file_name);

sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);

sprintf(oat_location_arg, "--oat-location=%s", output_file_name);

ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);

execl(DEX2OAT_BIN, DEX2OAT_BIN,

zip_fd_arg, zip_location_arg,

oat_fd_arg, oat_location_arg,

(char*) NULL);

ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));

}

這個(gè)函數(shù)定義在文件frameworks/native/cmds/installd/commands.c中。

其中,參數(shù)zip_fdoat_fd都是打開(kāi)文件描述符,指向的分別是正在安裝的APK文件和要生成的OAT文件。OAT文件的生成過(guò)程主要就是涉及到將包含在APK里面的classes.dex文件的DEX字節(jié)碼翻譯成本地機(jī)器指令。這相當(dāng)于是編寫一個(gè)輸入文件為DEX、輸出文件為OAT的編譯器。這個(gè)編譯器是基于LLVM開(kāi)發(fā)的。編譯器的工作原理比較高大上,所幸的是它不會(huì)影響到我們接下來(lái)的分析,因此我們就略過(guò)DEX字節(jié)碼翻譯成本地機(jī)器指令的過(guò)程,假設(shè)它很愉快地完成了。

    APK安裝過(guò)程中生成的OAT文件的輸入只有一個(gè)DEX文件,也就是來(lái)自于打包在要安裝的APK文件里面的classex.dex文件。實(shí)際上,一個(gè)OAT文件是可以由若干個(gè)DEX生成的。這意味著在生成的OAT文件的oatdata段中,包含有多個(gè)DEX文件。那么,在什么情況下,會(huì)生成包含多個(gè)DEX文件的OAT文件呢?

從前面Android ART運(yùn)行時(shí)無(wú)縫替換Dalvik虛擬機(jī)的過(guò)程分析一文可以知道,當(dāng)我們選擇了ART運(yùn)行時(shí)時(shí),Zygote進(jìn)程在啟動(dòng)的過(guò)程中,會(huì)調(diào)用libart.so里面的函數(shù)JNI_CreateJavaVM來(lái)創(chuàng)建一個(gè)ART虛擬機(jī)。函數(shù)JNI_CreateJavaVM的實(shí)現(xiàn)如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {

const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);

if (IsBadJniVersion(args->version)) {

LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;

return JNI_EVERSION;

}

Runtime::Options options;

for (int i = 0; i < args->nOptions; ++i) {

JavaVMOption* option = &args->options[i];

options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));

}

bool ignore_unrecognized = args->ignoreUnrecognized;

if (!Runtime::Create(options, ignore_unrecognized)) {

return JNI_ERR;

}

Runtime* runtime = Runtime::Current();

bool started = runtime->Start();

if (!started) {

delete Thread::Current()->GetJniEnv();

delete runtime->GetJavaVM();

LOG(WARNING) << "CreateJavaVM failed";

return JNI_ERR;

}

*p_env = Thread::Current()->GetJniEnv();

*p_vm = runtime->GetJavaVM();

return JNI_OK;

}

這個(gè)函數(shù)定義在文件art/runtime/jni_internal.cc中。

     參數(shù)vm_args用作ART虛擬機(jī)的啟動(dòng)參數(shù),它被轉(zhuǎn)換為一個(gè)JavaVMInitArgs對(duì)象后,再按照Key-Value的組織形式保存一個(gè)Options向量中,并且作該向量作為參數(shù)傳遞給Runtime類的靜態(tài)成員函數(shù)Create

    Runtime類的靜態(tài)成員函數(shù)Create負(fù)責(zé)在進(jìn)程中創(chuàng)建一個(gè)ART虛擬機(jī)。創(chuàng)建成功后,就調(diào)用Runtime類的另外一個(gè)靜態(tài)成員函數(shù)Start啟動(dòng)該ART虛擬機(jī)。注意,這個(gè)創(chuàng)建ART虛擬的動(dòng)作只會(huì)在Zygote進(jìn)程中執(zhí)行,SystemServer系統(tǒng)進(jìn)程以及Android應(yīng)用程序進(jìn)程的ART虛擬機(jī)都是直接從Zygote進(jìn)程fork出來(lái)共享的。這與Dalvik虛擬機(jī)的創(chuàng)建方式是完全一樣的。

接下來(lái)我們就重點(diǎn)分析Runtime類的靜態(tài)成員函數(shù)Create,它的實(shí)現(xiàn)如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

bool Runtime::Create(const Options& options, bool ignore_unrecognized) {

// TODO: acquire a static mutex on Runtime to avoid racing.

if (Runtime::instance_ != NULL) {

return false;

}

InitLogging(NULL); // Calls Locks::Init() as a side effect.

instance_ = new Runtime;

if (!instance_->Init(options, ignore_unrecognized)) {

delete instance_;

instance_ = NULL;

return false;

}

return true;

}

這個(gè)函數(shù)定義在文件art/runtime/runtime.cc中。

instance_Runtime類的靜態(tài)成員變量,它指向進(jìn)程中的一個(gè)Runtime單例。這個(gè)Runtime單例描述的就是當(dāng)前進(jìn)程的ART虛擬機(jī)實(shí)例。

    函數(shù)首先判斷當(dāng)前進(jìn)程是否已經(jīng)創(chuàng)建有一個(gè)ART虛擬機(jī)實(shí)例了。如果有的話,函數(shù)就立即返回。否則的話,就創(chuàng)建一個(gè)ART虛擬機(jī)實(shí)例,并且保存在Runtime類的靜態(tài)成員變量instance_中,最后調(diào)用Runtime類的成員函數(shù)Init對(duì)該新創(chuàng)建的ART虛擬機(jī)進(jìn)行初始化。

Runtime類的成員函數(shù)Init的實(shí)現(xiàn)如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) {

......

UniquePtr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized));

......

heap_ = new gc::Heap(options->heap_initial_size_,

options->heap_growth_limit_,

options->heap_min_free_,

options->heap_max_free_,

options->heap_target_utilization_,

options->heap_maximum_size_,

options->image_,

options->is_concurrent_gc_enabled_,

options->parallel_gc_threads_,

options->conc_gc_threads_,

options->low_memory_mode_,

options->long_pause_log_threshold_,

options->long_gc_log_threshold_,

options->ignore_max_footprint_);

......

java_vm_ = new JavaVMExt(this, options.get());

......

Thread* self = Thread::Attach("main", false, NULL, false);

......

if (GetHeap()->GetContinuousSpaces()[0]->IsImageSpace()) {

class_linker_ = ClassLinker::CreateFromImage(intern_table_);

} else {

......

class_linker_ = ClassLinker::CreateFromCompiler(*options->boot_class_path_, intern_table_);

}

......

return true;

}

這個(gè)函數(shù)定義在文件art/runtime/runtime.cc中。

    Runtime類的成員函數(shù)Init首先調(diào)用ParsedOptions類的靜態(tài)成員函數(shù)Create對(duì)ART虛擬機(jī)的啟動(dòng)參數(shù)raw_options進(jìn)行解析。解析后得到的參數(shù)保存在一個(gè)ParsedOptions對(duì)象中,接下來(lái)就根據(jù)這些參數(shù)一個(gè)ART虛擬機(jī)堆。ART虛擬機(jī)堆使用一個(gè)Heap對(duì)象來(lái)描述。

   創(chuàng)建好ART虛擬機(jī)堆后,Runtime類的成員函數(shù)Init接著又創(chuàng)建了一個(gè)JavaVMExt實(shí)例。這個(gè)JavaVMExt實(shí)例最終是要返回給調(diào)用者的,使得調(diào)用者可以通過(guò)該JavaVMExt實(shí)例來(lái)和ART虛擬機(jī)交互。再接下來(lái),Runtime類的成員函數(shù)Init通過(guò)Thread類的成員函數(shù)Attach將當(dāng)前線程作為ART虛擬機(jī)的主線程,使得當(dāng)前線程可以調(diào)用ART虛擬機(jī)提供的JNI接口。

Runtime類的成員函數(shù)GetHeap返回的便是當(dāng)前ART虛擬機(jī)的堆,也就是前面創(chuàng)建的ART虛擬機(jī)堆。通過(guò)調(diào)用Heap類的成員函數(shù)GetContinuousSpaces可以獲得堆里面的連續(xù)空間列表。如果這個(gè)列表的第一個(gè)連續(xù)空間是一個(gè)Image空間,那么就調(diào)用ClassLinker類的靜態(tài)成員函數(shù)CreateFromImage來(lái)創(chuàng)建一個(gè)ClassLinker對(duì)象。否則的話,上述ClassLinker對(duì)象就要通過(guò)ClassLinker類的另外一個(gè)靜態(tài)成員函數(shù)CreateFromCompiler來(lái)創(chuàng)建。創(chuàng)建出來(lái)的ClassLinker對(duì)象是后面ART虛擬機(jī)加載加載Java類時(shí)要用到的。

    后面我們分析ART虛擬機(jī)的垃圾收集機(jī)制時(shí)會(huì)看到,ART虛擬機(jī)的堆包含有三個(gè)連續(xù)空間和一個(gè)不連續(xù)空間。三個(gè)連續(xù)空間分別用來(lái)分配不同的對(duì)象。當(dāng)?shù)谝粋€(gè)連續(xù)空間不是Image空間時(shí),就表明當(dāng)前進(jìn)程不是Zygote進(jìn)程,而是安裝應(yīng)用程序時(shí)啟動(dòng)的一個(gè)dex2oat進(jìn)程。安裝應(yīng)用程序時(shí)啟動(dòng)的dex2oat進(jìn)程也會(huì)在內(nèi)部創(chuàng)建一個(gè)ART虛擬機(jī),不過(guò)這個(gè)ART虛擬機(jī)是用來(lái)將DEX字節(jié)碼編譯成本地機(jī)器指令的,而Zygote進(jìn)程創(chuàng)建的ART虛擬機(jī)是用來(lái)運(yùn)行應(yīng)用程序的。

接下來(lái)我們主要分析ParsedOptions類的靜態(tài)成員函數(shù)CreateART虛擬機(jī)堆Heap的構(gòu)造函數(shù),以便可以了解ART虛擬機(jī)的啟動(dòng)參數(shù)解析過(guò)程和ART虛擬機(jī)的堆創(chuàng)建過(guò)程。

ParsedOptions類的靜態(tài)成員函數(shù)Create的實(shí)現(xiàn)如下所示:


Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, bool ignore_unrecognized) {

UniquePtr<ParsedOptions> parsed(new ParsedOptions());

const char* boot_class_path_string = getenv("BOOTCLASSPATH");

if (boot_class_path_string != NULL) {

parsed->boot_class_path_string_ = boot_class_path_string;

}

......

parsed->is_compiler_ = false;

......

for (size_t i = 0; i < options.size(); ++i) {

const std::string option(options[i].first);

......

if (StartsWith(option, "-Xbootclasspath:")) {

parsed->boot_class_path_string_ = option.substr(strlen("-Xbootclasspath:")).data();

} else if (option == "bootclasspath") {

parsed->boot_class_path_

= reinterpret_cast<const std::vector<const DexFile*>*>(options[i].second);

} else if (StartsWith(option, "-Ximage:")) {

parsed->image_ = option.substr(strlen("-Ximage:")).data();

} else if (......) {

......

} else if (option == "compiler") {

parsed->is_compiler_ = true;

} else {

...... 


標(biāo)簽:

熱門專區(qū)

暫無(wú)熱門資訊

課程推薦

微信
微博
15311698296

全國(guó)免費(fèi)咨詢熱線

郵箱:codingke@1000phone.com

官方群:148715490

北京千鋒互聯(lián)科技有限公司版權(quán)所有   北京市海淀區(qū)寶盛北里西區(qū)28號(hào)中關(guān)村智誠(chéng)科創(chuàng)大廈4層
京ICP備2021002079號(hào)-2   Copyright ? 2017 - 2022
返回頂部 返回頂部