在 Android平台中采用Mono机制编译、处理C#语言编写的逻辑代码,编译之后本地存储IL指令。在游戏运行阶段存在代码动态编译的过程,原理为:利用 Unity3D引擎的 Mono jit机制将IL指令编译为机器可识别的汇编指令。

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.

引用官方的介绍,这部分不是本文的重点,有兴趣的读者可以自行前往官网了解。

Mono 的执行流程

首先我们从 mono的入口函数开始分析

大致运行流程如下

 Main() => mono_main_with_optinos() => mono_main() => mini_init() => mono_assembly_open() => main_thread_handler() => mini_cleanup() 

其中 main_thread_handler 函数主要负责编译 & 处理 IL 指令,执行流程如下

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 处理将要调用的方法,例如 ClassName::Main()。default_mono_runtime_invoke 函数实际调用 mono_jit_runtime_invoke 函数。mono_jit_runtime_invoke 函数调用来 mono_jit_compile_method_with_opt 实现编译目标函数的代码,调用mono_jit_compile_method 编译目标函数的 runtime wrapper (运行时的上层封装调用)。 runtime_invoke 函数调用编译之后的目标函数,其中 info->compiled_method 参数为编译之后目标函数代码在内存中的地址信息。

通过以上分析,mono_jit_compile_method_with_opt 函数为C#函数代码编译为目标机器指令的关键函数,我们来分析以下这部分的执行流程

mono_jit_compile_method_with_opt()

mono_jit_compile_method_inner()

mini_method_compile()

....

其中 mini_method_compile 函数在内部通过 Mono的 JIT 机制实现动态编译过程。

C#函数的执行过程

mono_runtime_invoke 函数实现与 mono \ object.c中,而实际调用 mono_jit_runtime_invoke 函数代码则在 mini.c 文件中。mono_runtime_invoke 函数负责实现 C#函数的代码编译和执行。

/**

* 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 函数会先通过查找列表,判断是否已创建对应的 info 信息,若不存在,则先进行编译并得倒 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 ++] = &params [i];

} else if (MONO_TYPE_IS_REFERENCE (t) || t->type == MONO_TYPE_PTR) {

args [pindex ++] = &params [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;

}

编译完成(或之前已经编译相同的 info 信息)后,则调用 info 自身的 runtime_invoke 函数实现对真正函数的调用。其中 runtime_invoke 函数的指针赋值。

compiled_method 变量存储目标函数编译之后所生成代码的内存地址。在之前的代码中,mono_jit_runtime_invoke 函数的第一个参数为 MonoMethod *method,在 MonoMethod 结构体中存储来需要编译和执行的 C# 函数的详细信息,包括:方法名、方法所属类的信息等。即根据 mono_jit_runtime_invoke 入口参数 method 可获取 compiled_method 变量存储的函数名,通过结构分析可以看到,在整个编译和调用过程中, MonoMethod 函数都是相关关键的结构,其结构在 class_internal.h 文件中定义。

。。。

1)name 存储 C# 函数名信息

2)klass 为 C# 函数所属类的相关信息。

通过 mono_jit_runtime_invoke 函数可知道 C# 函数的调用过程,以及函数地址、函数名、函数所属类等。

mono android 开机启动,浅析 Android 平台 mono执行机制 by郡墙相关推荐

  1. Android开机启动流程

    Android开机启动流程 一.APPS PBL(Application primary boot loader:主引导加载程序) 二.XBL(Extensible boot loader:可扩展引导 ...

  2. Android开机启动Activity或者Service方法

    这段时间在做Android的基础开发,现在有一需求是开机启动,按照网上某些博文教程做了下,始终不成功,一开机总是提示所启动的应用程序意外终止,于是参考了Android SDK doc,终于解决问题,下 ...

  3. Android开机启动流程简析

    Android开机启动流程简析 (一) 文章目录 Android开机启动流程简析 (一) 前言 一.开机启动的流程概述 二.Android的启动过程分析 (1).总体流程 init简述 Zygote简 ...

  4. Android开机启动的那些事

    以前知道AMS.PMS这些概念及其功能,开发的过程中也会用到,就是不知道其来源,好奇心害死猫,扒着扒着扒到系统开机启动这个知识层面上来了,好吧,那今天就说说这个吧! 系统开机启动过程 Android系 ...

  5. Android开机各个阶段(Android R)

    Android开机各个阶段(Android R) 目录 开机各个阶段描述 boot_progress_start boot_progress_preload_start boot_progress_p ...

  6. android开机图片格式,Android平台启动图使用.9.png图片

    概述 目前HBuilder|HBuilderX中仅定义几种标准分辨率的启动图配置,而实际上存在很多不同分辨率的手机,导致启动图会进行拉伸或压缩引起变形,Android平台为了解决此问题就出现了可以适配 ...

  7. Android8.0 开机启动脚本,Android开机启动shell脚本(Android 8.0测试OK)

    Android 下做开机启动shell脚本的大致流程如下: 目录 写shell脚本 为脚本写te文件 在init.rc中启动脚本 添加Selinux权限 写shell脚本 比如新建一个init.tes ...

  8. Android 开机启动shell脚本

    接到一个集成功能的需求,然后看了一下是由上层应用 + linux进程实现的功能,需要增加开机自动启动linux进程,没弄过有点懵. 这个不怎么正确,仅供参考,在权限那块需要更改,放到system下 环 ...

  9. Android开机启动流程初探

    l  Init进程 Android系统在启动时首先会启动Linux系统,引导加载Linux Kernel并启动init进程.Init进程是一个由内核启动的用户级进程,是Android系统的第一个进程. ...

  10. android开机启动service

    为什么80%的码农都做不了架构师?>>>    1.开机启动后系统会发射出一个Standard Broadcast Action,名字叫android.intent.action.B ...

最新文章

  1. 怎么设计计算机网络共享,如何设置网络共享 网络共享设置方法【详解】
  2. WPF ---- ​xmal 解析器没有办法解析类的TypeConverter
  3. SpringBoot拦截器与过滤器
  4. The Majesty Of Vue.js
  5. The World is a Theatre(组合数学)
  6. Arduino系列硬件资源介绍
  7. java selenium firefox启动报错大调查
  8. 应届生月薪2W,财务人却不涨薪?那是你不会这种财务分析
  9. 计算机主机结构3维图,台式主机内部结构图,主机结构图
  10. Feign的工作原理
  11. 芯片设计验证中遇到的VCD、VPD以及EVCD到底是什么?
  12. 解决刷了Gargoyle固件后某些LED灯不亮的问题
  13. session 失效 java.lang.IllegalStateException异常产生的原因及解决办法
  14. java中sql查重跟去重_SQL查重去重
  15. 龙芯CPU处理器和芯片资料介绍
  16. P1338 末日的传说(C++_数论_递推)
  17. 2018杭州云栖大会,梁胜博士的演讲PPT来啦! 1
  18. 《孙子兵法特殊战法之火攻篇》
  19. 2005年创新商业模式之窄告
  20. Cris 的 Python日记(一):基础语法

热门文章

  1. baddy:核心函数入口
  2. 自然语言(NLP)处理流程—IF-IDF统计—jieba分词—Word2Vec模型训练使用
  3. pd对焦速度_硬知识|都红圈了,为何对焦速度还很慢?你的镜头呢?
  4. Apollo代码学习(三)—车辆动力学模型
  5. Microsoft.VisualBasic.dll的妙用(开发中肯定会用到哦)
  6. [音乐天堂]辛德勒名单原声大碟
  7. 根据线索整理的一套在2021年继续使用Flash Player的方法(20.12.29更新)
  8. Taylor Swift - Mean-pdf
  9. 河北农业大学林学可转计算机系吗,河北农业大学专业排名,招生专业目录(10篇)...
  10. 记录一次实战破解无线wifi——Aircrack-ng