本文从Android init的main函数开始讲解。
1、 init进程
init进程启动是从源码system/core/init/main.cpp中的main()函数作为入口的,所以我们先来看看这个位置:
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv);
}
第一次启动main函数的时候没有携带参数,执行的是FirstStageMain(argc, argv);
改函数在system\core\init\first_stage_init.cpp
路径下,内容为:
int FirstStageMain(int argc, char** argv) {
.....
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
execv(path, const_cast<char**>(args));
.....
}
可以看到这里又再次执行了system/core/init/main.cpp
中的main()函数,且携带了selinux_setup
参数,回到main()函数,这次执行了SetupSelinux(argv)
,该函数在system\core\init\selinux.cpp
文件,内容如下:
// This function initializes SELinux then execs init to run in the init SELinux context.
int SetupSelinux(char** argv) {
InitKernelLogging(argv);
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
// Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging();
SelinuxInitialize();
// We're in the kernel domain and want to transition to the init domain. File systems that
// store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
// but other file systems do. In particular, this is needed for ramdisks such as the
// recovery image for A/B devices.
if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
}
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
// execv() only returns if an error happened, in which case we
// panic and never return from this function.
PLOG(FATAL) << "execv(\"" << path << "\") failed";
return 1;
}
可以看到这里又又次执行了system/core/init/main.cpp
中的main()函数,且携带了second_stage
参数,回到main()函数,这次执行了SecondStageMain(argc, argv)
,该函数在system\core\init\init.cpp
文件,内容如下:
int SecondStageMain(int argc, char** argv) {
....
LoadBootScripts(am, sm);
....
while (true) {
....
if (auto result = epoll.Wait(epoll_timeout); !result) {
LOG(ERROR) << result.error();
}
}
return 0;
}
这里可看到该函数先是执行了LoadBootScripts()
这个函数,之后阻塞在了epoll上。LoadBootScripts()
内容如下:
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
if (!parser.ParseConfig("/product_services/etc/init")) {
late_import_paths.emplace_back("/product_services/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
可以看到改函数解析了一个init.rc文件,改文件在system\core\rootdir\init.rc
目录下,其中有内容:
on zygote-start && property:ro.crypto.state=unencrypted
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
可以看到是想要启动zygote,这个zygote对应的.rc文件在同目录下,有不同位机器的脚本,以64位的init.zygote64.rc为例,有内容如下:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
可以看到这里似乎想启动一个/system/bin/app_process
程序,之后我们就来到了zygote进程,而原来的init进程上文已交代过了,解析完.rc文件之后便阻塞在了epoll上。/system/bin/app_process
这个程序的源文件目录为frameworks\base\cmds\app_process\app_main.cpp
同样看它的main()函数:
int main(int argc, char* const argv[])
{
....
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.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}
....
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
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.");
}
}
可以看到前文中启动app_process程序是带了--zygote
和--start-system-server
参数的,所以这里zygote = true
将会执行runtime.start("com.android.internal.os.ZygoteInit", args, zygote)
,runtime.start()函数实际上调用的是\frameworks\base\core\jni\AndroidRuntime.cpp
中的void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
函数,其内容如下:
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
....
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, 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).string());
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(&mJavaVM, &env, zygote)
创建了java虚拟机,然后使用startReg(env)
注册了jni的一些函数,因为之后到java环境后需要调用native层的一些函数。最后调用env->CallStaticVoidMethod(startClass, startMeth, strArray)
调用了java中的一个方法,这个方法是ZygoteInit.java
的main
方法。其内容如下:
@UnsupportedAppUsage
public static void main(String argv[]) {
....
try {
....
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
}
....
zygoteServer = new ZygoteServer(isPrimaryZygote);
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {
zygoteServer.closeServerSocket();
}
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}
这段代码有几处重要函数,功能如下:
preload(bootTimingsTraceLog); // 预加载类及一些资源文件,加快进程的启动
zygoteServer = new ZygoteServer(isPrimaryZygote); // 创建本地socket通信
Runnable r = forkSystemServer // 启动 SystemServer 进程
caller = zygoteServer.runSelectLoop(abiList); // 死循环,接收并处理AMS传过来的消息,比如fork app进程。
所以zygote分为native和java层,分别有如下功能:
native:
1.初始化运行环境,创建jvm Android的虚拟机
2.注册jni
3.调用 zygoteinit.main
java
1.预加载 -- 加快进程启动
2.创建sokcet通信
3.启动SystemServer进程
3.循环接收处理socket事件。