Android 系統啓動流程解析-從開機到Dalvik VM

Android完整的啓動流程,能夠理解爲從按下開機鍵到用戶最終看到Launcher的過程,這部分細節不少,力求瞭解大概流程,對關鍵細節掌握便可。本篇重點講解從開機到建立Dalvik VM的過程,下篇分析從Zygote到最終Launcher的顯示過程。java

介紹Android系統啓動流程的文章不少,在正式介紹以前,咱們能夠思考下,類比windows等PC系統的系統啓動流程,Android系統的啓動流程有何特別之處。linux

Android能夠理解爲構建於在Linux上的一個特殊「應用」,所以Android啓動的流程除了kernel的啓動部分以外,還有構建在它之上的Android運行環境的啓動部分。本篇主要介紹Kernel啓動到構建出Dalvik VM的過程。android

借用一張圖:windows

Android系統啓動流程

加電 & 系統自檢

當系統加電後,CPU復位會首先運行在ROM芯片內固化的一段指令(Boot ROM),這段指令會將BootLoader程序加載到內存中而且開始執行。緩存

BootLoader

BootLoader也叫「引導加載程序」,是個底層代碼,包含一堆指令,主要分爲兩部分,安全

  • 第一部分,檢測外部RAM,找到並加載另外一段bootloader程序,以後跳到其中執行。
  • 第二部分bootloader,設置運行內核所需的網絡、內存等基本條件,以後找到對應的Kernel鏡像文件,並將其加載到物理內存中。

關於鏡像文件,能夠參考:source.android.com/devices/boo…網絡

bootloader相關的源碼見:android/platform/bootable/bootloader/legacy/usbloader app

bootloader源碼

通過這一步,Kernel的相關鏡像已經加載到了物理內存的指定地址處,並創建了內核運行所需的基本環境。接下來BootLoade就將控制權交給了Kernel,內核開始執行socket

Kernel start階段

android kernel的加載過程與Linux Kernel加載過程相似,隨着內核啓動,開始設置緩存、受保護內存、調度和加載驅動程序。當完成這些設置後,便會啓動指定 /system/core/init 第一個用戶進程init。ide

相比Linux Kernel,Android Kernel新增了一些特性

  • Binder:android新增的一種進程間通訊機制;
  • Ashmem:android新增的共享內存方式;
  • Logger:對LogCat的內核支持;
  • WakeLocks:電源管理;
  • OOM處理:可用內存太低,會殺死進程;
  • Alarm Manager: 經過用戶控件通知內核什麼時候喚醒;
  • YAFFS2:針對閃存設備的文件系統;關於Android的文件系統,我以前也有一篇專門介紹

init進程

init進程是Android系統的第一個用戶進程,能夠說是root進程,它主要有兩個職責:

  1. 掛載/sys,/dev,/proc等文件目錄;
  2. 解析運行init.rc中的相關配置;init.rc腳本相關的語法可參考:android.googlesource.com/platform/sy…

相關源碼

以android 10源碼爲例,init進程的啓動主要分爲幾個階段:

//init進程入口
/init/main.cpp int main(int argc, char** argv) {
	f (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);
}

//第一階段
/init/first_stage_init.cpp int FirstStagetMain(int argc, char** argv) {
	//主要是建立掛載相關文件目錄
	CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
	CHECKCALL(mkdir("/dev/pts", 0755));
	CHECKCALL(mkdir("/dev/socket", 0755));
	CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
	CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" 	MAKE_STR(AID_READPROC)));
	...
	//------------執行selinux_setup-->main.cpp
	const char* path = "/system/bin/init";
	const char* args[] = {path, "selinux_setup", nullptr};
	execv(path, const_cast<char**>(args));
	...
}

//創建安全機制
/init/selinux.cpp: 執行一些安全策略
/ This function initializes SELinux then execs init to run in the init SELinux context.
int SetupSelinux(char** argv) {
	// Set up SELinux, loading the SELinux policy.
	SelinuxSetupKernelLogging();
	SelinuxInitialize();
	
	//--------------進入second_stage main.cpp
	const char* path = "/system/bin/init";
	const char* args[] = {path, "second_stage", nullptr};
	execv(path, const_cast<char**>(args));
}

//第二階段
/init/init.cpp int SecondStageMain(int argc, char** argv) {
	//初始化日誌系統
	InitKernelLogging(argv);
	//初始化屬性域
	property_init();
	//裝載子進程信號處理:爲了防止殭屍子進程沒法回收
	InstallSignalFdHandler(&epoll);
	//開啓屬性服務
	StartPropertyService(&epoll);
	//加載腳本
	ActionManager& am = ActionManager::GetInstance();
	ServiceList& sm = ServiceList::GetInstance();
	//加載解析init.rc腳本
	LoadBootScripts(am, sm);
	...
}

//解析init.rc文件
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");
	}
	...
}

複製代碼

解析init.rc

init.rc是個配置文件,具體語法能夠參考: android.googlesource.com/platform/sy…

在手機目錄下也能夠找到針對32位和64位的rc文件:

init.rc

這裏不分析init.rc文件的細節,解析init.rc後主要完成了如下幾件事情:

  • 建立一些關鍵文件目錄、設置權限策略;
  • 開啓ServiceManager、VndServiceManager等本地守護服務;
  • fork出Zygote進程;

Zygote進程

經過解析init.rc,從init進程fork出Zygote, 而且指定啓動入口在app_main.cpp

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
複製代碼
//----Zygote進程入口-----
/frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]){
	//構造AppRuntime
	AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
	argc--;
	argv++;
	//while循環拼接參數:根據init.rc中的配置,這裏zygote=true
	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;
		}
	}
	//根據前面init.rc指定傳入參數,
	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.");
	}
}

//AndroidRuntime 繼承自 AppRuntime
/frameworks/base/core/jni/AndroidRuntime.cpp
AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
mExitWithoutCleanup(false),
mArgBlockStart(argBlockStart),
mArgBlockLength(argBlockLength)
{
	//初始化繪製引擎skia
	SkGraphics::Init();
	// 虛擬機參數
	mOptions.setCapacity(20);
	//一個進程gCurRuntime只能初始化一次
	assert(gCurRuntime == NULL);        // one per process
	gCurRuntime = this;
}

void AndroidRuntime::start(const char className, const Vector <String8> & options, bool zygote)
{
	...
	/* start the virtual machine */
	//初始化JNI
	JniInvocation jni_invocation;
	jni_invocation.Init(NULL);
	JNIEnv env;
	//啓動VM
	if (startVm(&mJavaVM, &env, zygote) != 0) {
		return;
	}
	
	//建立成功調用
	onVmCreated(env);
	/*
	* 註冊預約義的JNI
	*/
	if (startReg(env) < 0) {
		return;
	}
	
	/*
	* Start VM.  This thread becomes the main thread of the VM, and will
	* not return until the VM exits.
	*/
	//入口類
	char slashClassName = toSlashClassName(className);
	jclass startClass = env->FindClass(slashClassName);
	if (startClass == NULL) {
	
	} else {
		//找到入口方法, ZygoteInit.main()
		jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
	    if (startMeth == NULL) {
	    } else {
		    //Native調用Java,至此進入Java
	    	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");
}

複製代碼

小結:init經過解析init.rc後fork出Zygote進程,以後Zygote在其main中處理完一堆參數後,初始化出一個AndroidRuntime對象,在構造AndroidRuntime對象時就會初始化Skia繪製引擎,而後調用start建立Dalvik(ART)虛擬機,註冊上層須要的JNI函數,找到並調用Java層入口類ZygoteInit.main()。至此,進入了Java世界。

總結

Android Kernel啓動過程與大多數系統啓動相似,從Boot ROM(PC BIOS)--> BootLoader-->Kernel 自啓-->第一個進程(init);只不過android Kernel相比Linux,多了一些Ashmem、Binder驅動之類的,同時須要爲構建Android上層環境作準備,所以init在解析init.rc文件的配置過程當中,會啓動一些守護服務,同時fork出Zygote進程,Zygote做爲鏈接Kernel與上層世界的橋樑,這裏建立處了Dalvik(ART) VM,註冊好JNI方法,經過Native調用並加載ZygoteInit.main(),進入了真正的Java世界。

下篇繼續分析從ZygoteInit.mai()到Launcher界面的顯示

相關文章
相關標籤/搜索