专栏名称: 看雪学苑
致力于移动与安全研究的开发者社区,看雪学院(kanxue.com)官方微信公众帐号。
目录
相关文章推荐
51好读  ›  专栏  ›  看雪学苑

安卓启动流程初探

看雪学苑  · 公众号  · 互联网安全  · 2025-03-29 17:59

正文

请到「今天看啥」查看全文


大致流程


引导加载程序-内核启动-init进程启动-Zygote进程启动-System Server进程-启动框架和用户界面-应用程序启动。


静态·关于安卓的架构

总体上,安卓的架构分布底层的内核空间以Linux Kernel为基础,上层的用户空间由Native系统库、虚拟机运行环境(ART)、框架层(Java API Framework)组成。两层空间由系统调用(Syscall)联通。


安卓架构示意图


动态·安卓系统启动过程

相比于静态去看安卓的架构,任何系统是实时运行,不断交互的,逐步分析,庖丁解牛,可以发现架构间各层次的关系。



Loader · 按下电源的第一关

Boot Rom是记录了引导启动的只读寄存器(Read Only Memory),按下电源键后,改变引导芯片开始从固化在ROM里的预设代码开始执行,然后加载引导程序到RAM。。


随后,来到Boot  Loader(我们熟悉的bl锁说的就是这里)启动安卓系统前的引导程序。 Bootloader有时被锁定,以防止未经授权的系统修改。


Kernel · 内核初始化


◆最先启动的是内核的 swapper 进程(又称为idle进程,pid = 0)负责初始化进程管理、内存管理,加载Display,Camera Driver,Binder Driver等相关工作。


◆kthreadd进程(又称为内核进程 pid = 2)负责创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程。 kthreadd进程是所有内核进程的鼻祖。


HAL · 硬件抽象层

Hardware Abstraction Layer  其中有多个库模块(蓝牙wifi等),当java框架层要访问硬件就会用到这。


Android Runtime & native · ART虚拟机和C++本地的初始化

其中涉及到了我们最想关注的安卓层面的加载流程,虚拟机的作用就不再多说,经过swapper进程在native层启动了Init进程(pid = 1,也就是安卓源码中的init.cpp) init进程是所有用户进程的鼻祖 为什么这么说,看他的作用。


◆init进程会孵化出ueventd、logd、healthd、installd、adbd、lmkd等用户守护进程;


◆init进程还启动 servicemanager (binder服务管家)、 bootanim (开机动画)等重要服务


◆init进程孵化出Zygote进程,Zygote进程是Android系统的第一个Java进程(即虚拟机进程), Zygote是所有Java进程的父进程 ,Zygote进程本身是由init进程孵化而来的。


Framework 层

在此层次中, init 进程解析了 init.rc 文件 fork 生成了关键的 Zygote 进程(翻译为受精卵,此进程也称为安卓的应用进程孵化器,是所有应用进程的父进程)对于此孵化进程,他会加载。


◆加载ZygoteInit类,注册Zygote Socket服务端套接字

◆加载虚拟机

◆提前加载类preloadClasses

◆提前加载资源preloadResouces


Zygote孵化的第一个进程是 System Server ,此进程负责启动与管理整个 Java Framework 包含了ActivityManager,WindowManager,PackageManager,PowerManager等服务。


Media Server不是由Zygote进程孵化而来的,而是由init生成,他负责启动与管理 C++ framework 包含AudioFlinger,Camera Service等服务。


APP层

Zygote进程孵化出的第一个app进程为Launcher,通俗来讲就是我们的桌面app;所有的app进程都是由zygote进程fork而来的。


Syscall && JNI · 两个连接

第一个系统调用是native与内核之间的联系,第二个JNI是java层与native层之间的联系。


启动流程源码细节

init.cpp的大致流程

从内核启动的第一个进程init开始看,我们在相应的目录,也就是安卓源码/system/core/init/目录下的init.cpp文件。



关注main函数,可以大致把init的加载流程分为一下几个部分。


程序入口,判断调用uevent还是看门狗守护进程。


int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}

if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}

if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}

} // namespace android


第一阶段又分为以下几个部分


◆判断是否为第一阶段(也就是if条件语句的这一部分)第二部分明明在下面为什么还要判断?因为当我们执行到第一阶段最后时会有一个切换到第二阶段的擦欧总,重新加载init的mian函数,再次进入时就不会进入第一阶段了。


◆加载必要文件:使用 mount 挂载 tmpfs , proc , sysfs , selinuxfs 等虚拟文件系统;创建设备节点(如 /dev/kmsg , /dev/random


◆设置SELinux:初始化SELinux( selinux_initialize(true) ),加载安全策略;恢复 /init 文件的上下文: selinux_android_restorecon("/init", 0)


◆切换第二阶段,并实现域的转换:设置环境变量: setenv("INIT_SECOND_STAGE", "true", 1) ;使用 execv 重新启动 /init 程序,将域从 kernel 切换为 init


【最后步骤很关键,重新启动init的过程也讲加载的权限进一步降低,在整个第一阶段都是以权限极高的kernel执行,转化为init域后权限降低,更为安全,这个就是SELinux安全策略的作用】


    add_environment("PATH", _PATH_DEFPATH);

bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);//判断第一阶段

if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();

// Clear the umask.
umask(0);
//这里加载必要的虚拟文件

// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
// Don't expose the raw commandline to unprivileged processes.
chmod("/proc/cmdline", 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));

// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
InitKernelLogging(argv);

LOG(INFO) << "init first stage started!";

if (!DoFirstStageMount()) {
LOG(ERROR) << "Failed to mount required partitions early ...";
panic();
}

SetInitAvbVersionInRecovery();
//这里设置SELinux安全策略
// Set up SELinux, loading the SELinux policy.
selinux_initialize(true);

//恢复/init文件上下文
// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
if (selinux_android_restorecon("/init", 0) == -1) {
PLOG(ERROR) << "restorecon failed";
security_failure();
}
//说明已经执行过了第一阶段
setenv("INIT_SECOND_STAGE", "true", 1);

static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);

char* path = argv[0];
char* args[] = { path, nullptr };
//重新启动init程序/经过此步骤的重新加载,权限降低
execv(path, args);

// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG (ERROR) << "execv(\"" << path << "\") failed";
security_failure();
}


第二阶段分为以下几个部分


◆文件属性的初始化

◆重新初始化SELinux,/init执行在init域权限更低,提供完整的保护策略

◆进一步解析脚本,在此过程中解析init.rc的操作是加载Zygote进程的关键部分值得重点分析

◆开启了事件管理与服务启动(也就是 ActionManager ServiceManager


至此章节开始的流程图初始化的Zygote和ServiceManager步骤就知道是在哪里实现的了。


//文件与属性的初始化操作
// At this point we're in the second stage of init.
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";

// Set up a session keyring that all processes will have access to. It
// will hold things like FBE encryption keys. No process should override
// its session keyring.
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);

// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

property_init();

// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
process_kernel_dt();
process_kernel_cmdline();

// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();

// Make the time that init started available for bootstat to log.
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));

// Set libavb version for Framework-only OTA match in Treble build.
const char* avb_version = getenv("INIT_AVB_VERSION");
if (avb_version) property_set("ro.boot.avb_version", avb_version);

// Clean up our environment.
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");

//重新初始化SELinux

// Now set up SELinux for second stage.
selinux_initialize(false);
selinux_restore_context();

//事件与信号处理
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
PLOG(ERROR) << "epoll_create1 failed";
exit(1);
}

signal_handler_init();
//进一步解析脚本
property_load_boot_defaults();
export_oem_lock_status();
start_property_service();
set_usb_controller();

const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);

ActionManager& am = ActionManager::GetInstance();
ServiceManager& sm = ServiceManager::GetInstance();
Parser& parser = Parser::GetInstance();

parser.AddSectionParser("service", std::make_unique(&sm));
parser.AddSectionParser("on", std::make_unique(&am));
parser.AddSectionParser("import", std::make_unique(&parser));
//解析init.rc脚本,我们最关注的Zygote进程会在这里进一步初始化
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
} else {
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(true);
parser.set_is_vendor_etc_init_loaded(true);
parser.set_is_odm_etc_init_loaded(true);
}

//事件管理与服务启动

// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();

am.QueueEventTrigger("early-init");

// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");

// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");

// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}

// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");


最后一部分是个主循环:


    while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1;

if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
}

if (!(waiting_for_prop || sm.IsWaitingForExec())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || sm.IsWaitingForExec())) {
if (!shutting_down) restart_processes();

// If there's a process that needs restarting, wake up in time for that.
if (process_needs_restart_at != 0) {
epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}

// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
}

epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}

return 0;
}


init.rc生成Zygote进程细节

Zygote的执行过程大致可以概括如下图,我们简单分析一下涉及到的源码和函数,蓝色加深的是涉及的三个源码。



从上文我们知道了Zygote进程是在init.rc中创建的,他对应的可执行程序叫app_process,对应的源文件是app_main.cpp。


app_main.cpp


较为关键的几个地方就是开始的AppRuntime初始化, 是后续启动 Android 平台服务或应用程序的核心接口。他是AndroidRuntime的子类,通过调用会通过继承的方法调用AndroidRuntime中的方法。


int main(int argc, char* const argv[])
{
if (!LOG_NDEBUG) {
String8 argv_String;
for (int i = 0 ; i < argc; ++i) {
argv_String.append("\"");
argv_String.append(argv[i]);
argv_String.append("\" ");
}
ALOGV("app_process main with argv: %s", argv_String.c_str());
}

AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
argc--;
argv++;


const char* spaced_commands[] = { "-cp", "-classpath" };
// Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
bool known_command = false;

int i;
for (i = 0; i < argc; i++) {
if (known_command == true) {
runtime.addOption(strdup(argv[i]));

ALOGV("app_process main add known option '%s'", argv[i]);
known_command = false;
continue;
}

for (int j = 0;
j < static_cast(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
++j) {
if (strcmp(argv[i], spaced_commands[j]) == 0) {
known_command = true;
ALOGV("app_process main found known command '%s'", argv[i]);
}
}

if (argv[i][0] != '-') {
break;
}
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}

runtime.addOption(strdup(argv[i]));

ALOGV("app_process main add option '%s'", argv[i]);
}
//运行模式
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;

//提取argv中的模式与参数
++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName = (arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className = arg;
break;
} else {
--i;
break;
}
}

Vector args;
if (!className.empty()) {
// We're not in zygote mode, the only argument we need to pass
// to RuntimeInit is the application argument.
//
// The Remainder of args get passed to startup class main(). Make
// copies of them before we overwrite them with the process name.
args.add(application ? String8("application") : String8("tool"));
runtime.setClassNameAndArgs(className, argc - i, argv + i);

if (!LOG_NDEBUG) {
String8 restOfArgs;
char* const* argv_new = argv + i;
int argc_new = argc - i;
for (int k = 0; k < argc_new; ++k) {
restOfArgs.append("\"");
restOfArgs.append(argv_new[k]);
restOfArgs.append("\" ");
}
ALOGV("Class name = %s, args = %s", className.c_str(), restOfArgs.c_str());
}
} else {
//这里是在Zygote模式
// We're in zygote mode.
maybeCreateDalvikCache();//确保 Dalvik 缓存存在(如有必要则创建)

if (startSystemServer) {
args.add(String8("start-system-server"));
}

char prop[PROP_VALUE_MAX];
if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
ABI_LIST_PROPERTY);
return 11;
}

String8 abiFlag("--abi-list=");
abiFlag.append(prop);//从系统属性中获取 ABI 列表(支持的架构),并将其作为 --abi-list 选项传递给 Zygote。
args.add(abiFlag);

// In zygote mode, pass all remaining arguments to the zygote
// main() method.
for (; i < argc; ++i) {
args.add(String8(argv[i]));
}
}

if (!niceName.empty()) {
runtime.setArgv0(niceName.c_str(), true /* setProcName */);
}
//最后的部分是启动了java中的这个类/或者是独立模式
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (!className.empty()) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}

AndroidRuntime.cpp

我们关注start中的函数,

  1. 最开始的是一些基础配置的虚拟化,

  2. 再往下就是虚拟机的初始化涉及了startVm函数

    1. **zygote** 参数:表示是否是由 zygote 进程派生启动的。 zygote 是 Android 启动的核心进程。

    2. **primary_zygote** :用于区分主 zygote 和次级 zygote

  3. 创建虚拟机后


void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
//启动的预备阶段,配置一些基础
ALOGD(">>>>>> START %s uid %d <<<<<,
className != NULL ? className : "(unknown)", getuid());

static const String8 startSystemServer("start-system-server");
// Whether this is the primary zygote, meaning the zygote which will fork system server.
bool primary_zygote = false;

/*
* 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/

for (size_t i = 0; i < options.size(); ++i) {
if (options[i] == startSystemServer) {
primary_zygote = true;
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
}

const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /system does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}

const char* artRootDir = getenv("ANDROID_ART_ROOT");
if (artRootDir == NULL) {
LOG_FATAL("No ART directory specified with ANDROID_ART_ROOT environment variable.");
return;
}

const char* i18nRootDir = getenv("ANDROID_I18N_ROOT" );
if (i18nRootDir == NULL) {
LOG_FATAL("No runtime directory specified with ANDROID_I18N_ROOT environment variable.");
return;
}

const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT");
if (tzdataRootDir == NULL) {
LOG_FATAL("No tz data directory specified with ANDROID_TZDATA_ROOT environment variable.");
return;
}

//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);

//启动虚拟机
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
//这里的startVm函数启动了虚拟机(ART或者Dalvik)
if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
return;
}
onVmCreated(env); //虚拟机进一步配置与注册

/*
* Register android functions.
*/

//注册本地方法 这个应该也很关键吧
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}

/*
* We want to call main() with a String array with arguments in it.
* At present we have two arguments, the class name and an option string.
* Create an array to hold them.
*/

jclass stringClass;
jobjectArray strArray;
jstring classNameStr;

stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);

for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).c_str());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}

/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/

char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);

ALOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
ALOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
ALOGW("Warning: VM did not shut down cleanly\n");
}

startVm函数


此函数的体量过于庞大。主要的功能是设置了虚拟机的参数,利用了很多的parseRuntimeOption函数(对于不同的软硬件环境,函数的参数往往需要调整、优化,从而使系统达到最佳性能)。


安卓6到10对源码进行了一些调整 但是基础功能并未做出太大变动,一下是gityuan师傅的安卓6.0的代码注释。


int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
// JNI检测功能,用于native层调用jni函数时进行常规检测,比较弱字符串格式是否符合要求,资源是否正确释放。该功能一般用于早期系统调试或手机Eng版,对于User版往往不会开启,引用该功能比较消耗系统CPU资源,降低系统性能。
bool checkJni = false;
property_get("dalvik.vm.checkjni", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
checkJni = true;
} else if (strcmp(propBuf, "false") != 0) {
property_get("ro.kernel.android.checkjni", propBuf, "");
if (propBuf[0] == '1') {
checkJni = true;
}
}
if (checkJni) {
addOption("-Xcheck:jni");
}

//虚拟机产生的trace文件,主要用于分析系统问题,路径默认为/data/anr/traces.txt
parseRuntimeOption("dalvik.vm.stack-trace-file", stackTraceFileBuf, "-Xstacktracefile:");

//对于不同的软硬件环境,这些参数往往需要调整、优化,从而使系统达到最佳性能
parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");
parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");
parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree=");
parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");
parseRuntimeOption("dalvik.vm.heaptargetutilization",
heaptargetutilizationOptsBuf, "-XX:HeapTargetUtilization=");
...
////这一部分在安卓10中没有找到对应相同代码
//preloaded-classes文件内容是由WritePreloadedClassFile.java生成的,
//在ZygoteInit类中会预加载工作将其中的classes提前加载到内存,以提高系统性能
if (!hasFile("/system/etc/preloaded-classes")) {
return -1;
}
////这一步还是保留了
//初始化虚拟机
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}
}

startReg函数


这个函数层层套用,最终实现的功能就是启动一些native层的函数方法的注册。


int AndroidRuntime::startReg(JNIEnv* env)
{
//设置线程创建方法为javaCreateThreadEtc 【见小节2.4.1】
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

env->PushLocalFrame(200);
//进程NI方法的注册【见小节2.4.2】
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
return 0;
}
//————————————————————————————这里就是进程NI方法的注册
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) {
for (size_t i = 0; i < count; i++) {
//【见小节2.4.3】
if (array[i].mProc(env) < 0) {
return -1;
}
}
return 0;
}
//————————————————————————————上一层的array[i].mProc(env) 实际上就是这个数组中的不同函数
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_android_os_Binder),
...
};
//————————————————————————————
#define REG_JNI(name) { name }
struct RegJNIRec {
int (*mProc)(JNIEnv*);
};

进入java层, 书接上文提到的AndroidRuntime.cpp的最后步骤,也就是


    char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);//查找java的类
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {//查找类中的静态方法main
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {//使用JNI待用静态方法main
env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);


这里说的main就是 ZygoteInit.main() (我们在app_main.cpp的最后是start了 com.android.internal.os.ZygoteInit 这个包,接下来就是去调用他的main)


public static void main(String argv[]) {
try {//实际上这个DDMS是可以在Dalvik和ART两种虚拟机中运行的
RuntimeInit.enableDdms(); //开启DDMS功能,Dalvik Debug Monitor Service,为安卓SDK提供的调试工具
SamplingProfilerIntegration.start();
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
...

registerZygoteSocket(socketName); //为Zygote注册socket【见小节3.2】
preload(); // 预加载类和资源
SamplingProfilerIntegration.writeZygoteSnapshot();
gcAndFinalize(); //GC操作garbage collection 垃圾回收操作
if (startSystemServer) {
startSystemServer(abiList, socketName);//启动system_server【见小节3.4】
}
runSelectLoop(abiList); //进入循环模式【见小节3.5】
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run(); //启动system_server中会讲到。
} catch (RuntimeException ex) {
closeServerSocket();
throw ex;
}
}


在mian中是先开启了DDMS功能 然后为Zygote注册socket,preload函数预加载类和资源,启动SystemServer进程。


最后用一张流程图概括Zygote的总体加载流程就很清楚了



1.解析init.zygote.rc中的参数,创建AppRuntime并调用AppRuntime.start()方法;


2.调用AndroidRuntime的startVM()方法创建虚拟机,再调用startReg()注册JNI函数;


3.通过JNI方式调用ZygoteInit.main(),第一次进入Java世界;


4.registerZygoteSocket()建立socket通道,zygote作为通信的服务端,用于响应

客户端请求;


5.preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率;


6.zygote完毕大部分工作,接下来再通过startSystemServer(),fork得力帮手system_server进程,也是上层framework的运行载体。


7.zygote功成身退,调用runSelectLoop(),随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作。


参考文章

本文参考了很多Gityuan大师傅的博客文章,系统讲解了安卓的知识。

Android 操作系统架构开篇 - Gityuan博客 | 袁辉辉的技术博客

Android系统启动-zygote篇 - Gityuan博客 | 袁辉辉的技术博客

Android系统启动-Init篇 - Gityuan博客 | 袁辉辉的技术博客

安卓源码



图片


看雪ID:xianyuuuan

https://bbs.kanxue.com/user-home-1002959.htm

*本文为看雪论坛优秀文章,由 xianyuuuan 原创,转载请注明来自看雪社区



# 往期推荐

1、 一种基于unicorn的寄存器间接跳转混淆去除方式

2、 白盒SM4的DFA方案

3、 VNCTF-2025-赛后复现

4、 IDA Pro 9 SP1 安装和插件配置

5、 初探 android crc 检测及绕过

图片



球分享

球点赞

球在看



点击阅读原文查看更多







请到「今天看啥」查看全文