mono android 开机启动,浅析 Android 平台 mono执行机制 by郡墙
在 Android平臺(tái)中采用Mono機(jī)制編譯、處理C#語(yǔ)言編寫的邏輯代碼,編譯之后本地存儲(chǔ)IL指令。在游戲運(yùn)行階段存在代碼動(dòng)態(tài)編譯的過(guò)程,原理為:利用 Unity3D引擎的 Mono jit機(jī)制將IL指令編譯為機(jī)器可識(shí)別的匯編指令。
Mono 是什么?
Sponsored by Microsoft, Mono is an open source implementation of Microsoft's .NET Framework based on the ECMA standards for C# and the Common Language Runtime. A growing family of solutions and an active and enthusiastic contributing community is helping position Mono to become the leading choice for development of cross platform applications.
引用官方的介紹,這部分不是本文的重點(diǎn),有興趣的讀者可以自行前往官網(wǎng)了解。
Mono 的執(zhí)行流程
首先我們從 mono的入口函數(shù)開始分析
大致運(yùn)行流程如下
Main() => mono_main_with_optinos() => mono_main() => mini_init() => mono_assembly_open() => main_thread_handler() => mini_cleanup()其中 main_thread_handler 函數(shù)主要負(fù)責(zé)編譯 & 處理 IL 指令,執(zhí)行流程如下
main_thread_handler()
=> mono_jit_exec()
=> mono_assembly_get_image() 得倒 image 信息
=> mono_runtime_run_main()
=> mono_thread_set_main()
=> mono_assembly_set_main()
=> mono_runtime_exec_main()
....
mono_runtime_invoke 處理將要調(diào)用的方法,例如 ClassName::Main()。default_mono_runtime_invoke 函數(shù)實(shí)際調(diào)用 mono_jit_runtime_invoke 函數(shù)。mono_jit_runtime_invoke 函數(shù)調(diào)用來(lái) mono_jit_compile_method_with_opt 實(shí)現(xiàn)編譯目標(biāo)函數(shù)的代碼,調(diào)用mono_jit_compile_method 編譯目標(biāo)函數(shù)的 runtime wrapper (運(yùn)行時(shí)的上層封裝調(diào)用)。 runtime_invoke 函數(shù)調(diào)用編譯之后的目標(biāo)函數(shù),其中 info->compiled_method 參數(shù)為編譯之后目標(biāo)函數(shù)代碼在內(nèi)存中的地址信息。
通過(guò)以上分析,mono_jit_compile_method_with_opt 函數(shù)為C#函數(shù)代碼編譯為目標(biāo)機(jī)器指令的關(guān)鍵函數(shù),我們來(lái)分析以下這部分的執(zhí)行流程
mono_jit_compile_method_with_opt()
mono_jit_compile_method_inner()
mini_method_compile()
....
其中 mini_method_compile 函數(shù)在內(nèi)部通過(guò) Mono的 JIT 機(jī)制實(shí)現(xiàn)動(dòng)態(tài)編譯過(guò)程。
C#函數(shù)的執(zhí)行過(guò)程
mono_runtime_invoke 函數(shù)實(shí)現(xiàn)與 mono \ object.c中,而實(shí)際調(diào)用 mono_jit_runtime_invoke 函數(shù)代碼則在 mini.c 文件中。mono_runtime_invoke 函數(shù)負(fù)責(zé)實(shí)現(xiàn) C#函數(shù)的代碼編譯和執(zhí)行。
/**
* mono_jit_runtime_invoke:
* \param method: the method to invoke
* \param obj: this pointer
* \param params: array of parameter values.
* \param exc: Set to the exception raised in the managed method.
* \param error: error or caught exception object
* If \p exc is NULL, \p error is thrown instead.
* If coop is enabled, \p exc argument is ignored -
* all exceptions are caught and propagated through \p error
*/
static MonoObject*
mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError *error)
{
MonoMethod *invoke, *callee;
MonoObject *(*runtime_invoke) (MonoObject *this_obj, void **params, MonoObject **exc, void* compiled_method);
MonoDomain *domain = mono_domain_get ();
MonoJitDomainInfo *domain_info;
RuntimeInvokeInfo *info, *info2;
MonoJitInfo *ji = NULL;
gboolean callee_gsharedvt = FALSE;
if (mono_use_interpreter)
return mini_get_interp_callbacks ()->runtime_invoke (method, obj, params, exc, error);
error_init (error);
if (exc)
*exc = NULL;
if (obj == NULL && !(method->flags & METHOD_ATTRIBUTE_STATIC) && !method->string_ctor && (method->wrapper_type == 0)) {
g_warning ("Ignoring invocation of an instance method on a NULL instance.\n");
return NULL;
}
domain_info = domain_jit_info (domain);
info = (RuntimeInvokeInfo *)mono_conc_hashtable_lookup (domain_info->runtime_invoke_hash, method);
//mono_jit_runtime_invoke 函數(shù)會(huì)先通過(guò)查找列表,判斷是否已創(chuàng)建對(duì)應(yīng)的 info 信息,若不存在,則先進(jìn)行編譯并得倒 info信息
if (!info) {
if (mono_security_core_clr_enabled ()) {
/*
* This might be redundant since mono_class_vtable () already does this,
* but keep it just in case for moonlight.
*/
mono_class_setup_vtable (method->klass);
if (mono_class_has_failure (method->klass)) {
mono_error_set_for_class_failure (error, method->klass);
if (exc)
*exc = (MonoObject*)mono_class_get_exception_for_failure (method->klass);
return NULL;
}
}
gpointer compiled_method;
callee = method;
if (method->klass->rank && (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) &&
(method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)) {
/*
* Array Get/Set/Address methods. The JIT implements them using inline code
* inside the runtime invoke wrappers, so no need to compile them.
*/
if (mono_aot_only) {
/*
* Call a wrapper, since the runtime invoke wrapper was not generated.
*/
MonoMethod *wrapper;
wrapper = mono_marshal_get_array_accessor_wrapper (method);
invoke = mono_marshal_get_runtime_invoke (wrapper, FALSE);
callee = wrapper;
} else {
callee = NULL;
}
}
if (callee) {
compiled_method = mono_jit_compile_method (callee, error);
if (!compiled_method) {
g_assert (!mono_error_ok (error));
return NULL;
}
if (mono_llvm_only) {
ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (compiled_method), NULL);
callee_gsharedvt = mini_jit_info_is_gsharedvt (ji);
if (callee_gsharedvt)
callee_gsharedvt = mini_is_gsharedvt_variable_signature (mono_method_signature (jinfo_get_method (ji)));
}
if (!callee_gsharedvt)
compiled_method = mini_add_method_trampoline (callee, compiled_method, mono_method_needs_static_rgctx_invoke (callee, TRUE), FALSE);
} else {
compiled_method = NULL;
}
info = create_runtime_invoke_info (domain, method, compiled_method, callee_gsharedvt, error);
if (!mono_error_ok (error))
return NULL;
mono_domain_lock (domain);
info2 = (RuntimeInvokeInfo *)mono_conc_hashtable_insert (domain_info->runtime_invoke_hash, method, info);
mono_domain_unlock (domain);
if (info2) {
g_free (info);
info = info2;
}
}
/*
* We need this here because mono_marshal_get_runtime_invoke can place
* the helper method in System.Object and not the target class.
*/
if (!mono_runtime_class_init_full (info->vtable, error)) {
if (exc)
*exc = (MonoObject*) mono_error_convert_to_exception (error);
return NULL;
}
/* If coop is enabled, and the caller didn't ask for the exception to be caught separately,
we always catch the exception and propagate it through the MonoError */
gboolean catchExcInMonoError =
(exc == NULL) && mono_threads_is_coop_enabled ();
MonoObject *invoke_exc = NULL;
if (catchExcInMonoError)
exc = &invoke_exc;
/* The wrappers expect this to be initialized to NULL */
if (exc)
*exc = NULL;
#ifdef MONO_ARCH_DYN_CALL_SUPPORTED
if (info->dyn_call_info) {
MonoMethodSignature *sig = mono_method_signature (method);
gpointer *args;
static RuntimeInvokeDynamicFunction dyn_runtime_invoke;
int i, pindex, buf_size;
guint8 *buf;
guint8 retval [256];
if (!dyn_runtime_invoke) {
invoke = mono_marshal_get_runtime_invoke_dynamic ();
dyn_runtime_invoke = (RuntimeInvokeDynamicFunction)mono_jit_compile_method (invoke, error);
if (!mono_error_ok (error))
return NULL;
}
/* Convert the arguments to the format expected by start_dyn_call () */
args = (void **)g_alloca ((sig->param_count + sig->hasthis) * sizeof (gpointer));
pindex = 0;
if (sig->hasthis)
args [pindex ++] = &obj;
for (i = 0; i < sig->param_count; ++i) {
MonoType *t = sig->params [i];
if (t->byref) {
args [pindex ++] = ¶ms [i];
} else if (MONO_TYPE_IS_REFERENCE (t) || t->type == MONO_TYPE_PTR) {
args [pindex ++] = ¶ms [i];
} else {
args [pindex ++] = params [i];
}
}
//printf ("M: %s\n", mono_method_full_name (method, TRUE));
buf_size = mono_arch_dyn_call_get_buf_size (info->dyn_call_info);
buf = g_alloca (buf_size);
g_assert (buf);
mono_arch_start_dyn_call (info->dyn_call_info, (gpointer**)args, retval, buf);
dyn_runtime_invoke (buf, exc, info->compiled_method);
mono_arch_finish_dyn_call (info->dyn_call_info, buf);
if (catchExcInMonoError && *exc != NULL) {
mono_error_set_exception_instance (error, (MonoException*) *exc);
return NULL;
}
if (info->ret_box_class)
return mono_value_box_checked (domain, info->ret_box_class, retval, error);
else
return *(MonoObject**)retval;
}
#endif
MonoObject *result;
if (mono_llvm_only) {
result = mono_llvmonly_runtime_invoke (method, info, obj, params, exc, error);
if (!is_ok (error))
return NULL;
} else {
runtime_invoke = (MonoObject *(*)(MonoObject *, void **, MonoObject **, void *))info->runtime_invoke;
result = runtime_invoke ((MonoObject *)obj, params, exc, info->compiled_method);
}
if (catchExcInMonoError && *exc != NULL)
mono_error_set_exception_instance (error, (MonoException*) *exc);
return result;
}
編譯完成(或之前已經(jīng)編譯相同的 info 信息)后,則調(diào)用 info 自身的 runtime_invoke 函數(shù)實(shí)現(xiàn)對(duì)真正函數(shù)的調(diào)用。其中 runtime_invoke 函數(shù)的指針賦值。
compiled_method 變量存儲(chǔ)目標(biāo)函數(shù)編譯之后所生成代碼的內(nèi)存地址。在之前的代碼中,mono_jit_runtime_invoke 函數(shù)的第一個(gè)參數(shù)為 MonoMethod *method,在 MonoMethod 結(jié)構(gòu)體中存儲(chǔ)來(lái)需要編譯和執(zhí)行的 C# 函數(shù)的詳細(xì)信息,包括:方法名、方法所屬類的信息等。即根據(jù) mono_jit_runtime_invoke 入口參數(shù) method 可獲取 compiled_method 變量存儲(chǔ)的函數(shù)名,通過(guò)結(jié)構(gòu)分析可以看到,在整個(gè)編譯和調(diào)用過(guò)程中, MonoMethod 函數(shù)都是相關(guān)關(guān)鍵的結(jié)構(gòu),其結(jié)構(gòu)在 class_internal.h 文件中定義。
。。。
1)name 存儲(chǔ) C# 函數(shù)名信息
2)klass 為 C# 函數(shù)所屬類的相關(guān)信息。
通過(guò) mono_jit_runtime_invoke 函數(shù)可知道 C# 函數(shù)的調(diào)用過(guò)程,以及函數(shù)地址、函數(shù)名、函數(shù)所屬類等。
總結(jié)
以上是生活随笔為你收集整理的mono android 开机启动,浅析 Android 平台 mono执行机制 by郡墙的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 计算机组成原理 — PCI-E 外设接口
- 下一篇: Load和Initialize的区别和使