DEX2OTA流程概述
dexopt()
/frameworks/native/cmds/installd/dexopt.cpp
包含了大量的判断及属性代码,其中run_dex2oat是将dex转化为oat的关键函数。
- pid_t pid = fork();这行代码调用fork()函数创建一个新的进程。fork()函数返回两次:在父进程中,它返回新创建子进程的PID,在子进程中,它返回0。
- if (pid == 0) { ... }这个if语句用于判断当前是在子进程中还是在父进程中。由于子进程中fork()返回0,所以这段代码只会在子进程中执行。
- else{
int res = wait_child(pid){
...
}
}
由于父进程中fork()返回0,所以这段代码只会在父进程执行。
在子进程中执行run_dex2oat,父进程中阻塞等待子进程结束
在C或C++语言中,wait_child函数通常用于父进程等待子进程结束并获取子进程的退出状态。这个函数通常在fork或exec等系统调用之后使用,以确保子进程完成任务后再继续执行父进程的代码。
以下是一个示例代码,展示了如何使用wait_child函数等待子进程结束:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid = fork(); // 创建一个子进程
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程执行的代码
printf("Child process running.../n");
sleep(2); // 模拟子进程执行任务
printf("Child process finished./n");
exit(EXIT_SUCCESS);
} else {
// 父进程执行的代码
printf("Parent process waiting for child.../n");
int res = wait_child(pid); // 等待子进程结束
if (res == -1) {
perror("wait_child failed");
exit(EXIT_FAILURE);
} else {
printf("Child process exited with status: %d/n", res);
}
}
return 0;
}
run_dex2oat()
大约99%的函数都是在做属性的判断以及参数的定义,最终确定了bin程序及所需参数。
execv(dex2oat_bin, (char * const *)argv);
execv是一个函数名,它属于Unix和类Unix操作系统(如Linux)中的系统调用。这个函数的目的是装入并运行其它程序的函数。它的基本语法是:int execv(const char *pathname, char * const argv[]),其中pathname是你想要执行的程序的路径,而argv是一个指针数组,其中每个指针指向一个参数字符串,这个数组必须以NULL指针结束。
execv函数在执行新的程序后,会替换当前进程的映像,加载并执行新的程序。这意味着在调用execv之后,当前的程序会停止运行,而新的程序(在给定的路径下)会在同一个进程内开始运行。新的程序可以访问当前进程的数据,但一旦execv返回,当前进程的代码和数据就会被丢弃。
fork的意义
dex2ota_bin
/art/dex2oat/dex2oat.cc
int main(int argc, char** argv) {
int result = static_cast<int>(art::Dex2oat(argc, argv));
// Everything was done, do an explicit exit here to avoid running Runtime destructors that take
// time (bug 10645725) unless we're a debug or instrumented build or running on valgrind. Note:
// The Dex2Oat class should not destruct the runtime in this case.
if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && (RUNNING_ON_MEMORY_TOOL == 0)) {
_exit(result);
}
return result;
}
dex2oat()
main函数中,调用了art命名空间下的dex2oat函数,在dex2oat()函数中,
- arm上的workaround由b13564922()完成,这个与dex2oat的主要线程无关,可以暂时忽略
- 构造Dex2oat对象——Dex2Oat dex2oat(&timings);
- 处理命令行参数,将dex文件名文件路径等参数传入函数中——dex2oat.ParseArgs(argc, argv);
- 判断对于dex文件是否有写的权限——dex2oat.OpenFile()
- 打印命令行参数——LOG(INFO) << CommandLine();
- 判断dex2oat编译环境是否启动成功——dex2oat.Setup()
- 根据是否image分别调用CompileImage(dex2oat)或CompileApp(dex2oat)的处理
// Helps debugging on device. Can be used to determine which dalvikvm instance invoked a dex2oat
// instance. Used by tools/bisection_search/bisection_search.py.
VLOG(compiler) << "Running dex2oat (parent PID = " << getppid() << ")";
dex2oat::ReturnCode result;
if (dex2oat->IsImage()) {
result = CompileImage(*dex2oat);
} else {
result = CompileApp(*dex2oat);
}
CompileApp()&&CompileImage()
两者代码基本相同,有一个共同的特点,就是都先调用了dex2oat.Compile()函数。
Compile()
// Create and invoke the compiler driver. This will compile all the dex files.
void Compile() {
TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
compiler_phases_timings_.reset(new CumulativeLogger("compilation times"));
// Handle and ClassLoader creation needs to come after Runtime::Create
jobject class_loader = nullptr;
Thread* self = Thread::Current();
if (!boot_image_option_.empty()) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
OpenClassPathFiles(runtime_->GetClassPathString(), dex_files_, &class_path_files_);
ScopedObjectAccess soa(self);
// Classpath: first the class-path given.
std::vector<const DexFile*> class_path_files;
for (auto& class_path_file : class_path_files_) {
class_path_files.push_back(class_path_file.get());
}
// Store the classpath we have right now.
key_value_store_->Put(OatHeader::kClassPathKey,
OatFile::EncodeDexFileDependencies(class_path_files));
// Then the dex files we'll compile. Thus we'll resolve the class-path first.
class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end());
class_loader = class_linker->CreatePathClassLoader(self, class_path_files);
}
driver_.reset(new CompilerDriver(compiler_options_.get(),
verification_results_.get(),
&method_inliner_map_,
compiler_kind_,
instruction_set_,
instruction_set_features_.get(),
image_,
image_classes_.release(),
compiled_classes_.release(),
nullptr,
thread_count_,
dump_stats_,
dump_passes_,
dump_cfg_file_name_,
compiler_phases_timings_.get(),
swap_fd_,
profile_file_));
driver_->CompileAll(class_loader, dex_files_, timings_);
}
之后便是对具体的dex文件进行complie,不再做具体分析。
WatchDog
WatchDog的开启时机在于dex2oat()中的ParseArgs(argc, argv);
- 设置编译选项compiler_options_.reset(new CompilerOptions());
- 对参数进行筛选检查
- 配置编译器
if (!ReadCompilerOptions(args, compiler_options_.get(), &error_msg)) {
Usage(error_msg.c_str());
}
ProcessOptions(parser_options.get());
// Insert some compiler things.
InsertCompileOptions(argc, argv);
在processOptions中对相关选项进行配置
其中有着关于WatchDog开关的配置
// Done with usage checks, enable watchdog if requested
if (parser_options->watch_dog_enabled) {
int64_t timeout = parser_options->watch_dog_timeout_in_ms > 0
? parser_options->watch_dog_timeout_in_ms
: WatchDog::kDefaultWatchdogTimeoutInMS;
watchdog_.reset(new WatchDog(timeout));
}
WatchDog::kDefaultWatchdogTimeoutInMS便是对dex2oat编译管控的具体时间
// 9.5 minutes scaled by kSlowdownFactor. This is slightly smaller than the Package Manager
// watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that dex2oat will abort
// itself before that watchdog would take down the system server.
static constexpr int64_t kWatchDogTimeoutSeconds = kWatchdogSlowdownFactor * (9 * 60 + 30);
static constexpr int64_t kDefaultWatchdogTimeoutInMS =
kWatchdogVerifyMultiplier * kWatchDogTimeoutSeconds * 1000;