浅析Hello World源码是如何被Mono执行

开发 后端
很多程序员可能是第一次接触开源项目Mono,今天我们就从最简单的Hello World源码开始分析Mono的执行机制。

本文目的很简单,简单分析一下下面语句是如何被Mono Runtime执行的:  

  1. public class Hello1   
  2.  {   
  3.   public static void Main()   
  4.  {   
  5.      System.Console.WriteLine("Hello, World!");   
  6.   }   
  7. }  

由于自己也是刚接触Mono源码,以前一直停留于简单的应用之上,所以本文的分析不全面,只是为了能大致的了解Mono的执行流程,对基本的函数和流程有所了解,所以本文的风格为流水帐。

一、先简单的看下Mono源码的目录中的几个重要文件夹:

我这里使用的是不知道什么时候下载的某个历史版本2.6.7,目前我觉得重要的就3个文件夹,mcs为base framework这里除了常见的System namespace下的各种重要程序集之外还有以Mono作为namespace的一些程序集,听雨痕说玩Mono不能错过这些程序集,有空看下;libgc文件夹里貌似是提供GC的支持库;最重要一个文件夹就是mono了,下面着重看下这个目录,这里实现的是Mono Runtime,本文调试的代码也主要是这里的。

1.arch:此目录放了各种不同处理器的差异代码,看下x86里面就两个文件x86-codegen.h和tramp.c,这个tramp.c通过调用x86-codegen.h里的各种奇怪的宏提供了两个方法mono_arch_create_method_pointer和mono_arch_create_trampoline,依这个名字看是由MonoMethod *method生成一个本机代码的指针。。。不过这两个方法太过抽象还没看懂,先放到一边。

2.io-layer:从这个目录下的各个文件名来看,这个是和操作系统打交道的,比如posix,socket,threads,semaphores,io等等,这个我很感兴趣,特别是P/Invoke是如何和这些打交道的,这一块在以后也可以做深入的分析。

3.metadata:这一块是实现cli的,比如appdomain,assembly,class&object,cominterop,exception等等,不过还有gc,coree,filewatcher等等,看来这里不只是cli的实现,还有一堆其他的东西。

4.mini:这个名字起的很奇怪,为啥叫mini呢?我猜可能是为了和mono对应吧。。。其实这里是Mono Runtime里最基础的部分,比如aot,Linear IR的jit,debugger等等,当然main函数也再这里。

5.utils和其他:utils提供一些基础设施方面的功能,比如内存分配方面的,常见的数据结构实现,以及各种工具方法。其他的几个文件夹我暂时没发现有价值的东西,先搁在一边。

二、切入正题,打断点分析源码

1.***步尝试:找出编译的步骤和执行的入口,单步来分析执行流程。在mono_main() at driver.c里找到了main_thread_handler方法,这个是mono在解析完一系列args后的调用,这个方法是入口,接着往下看,由于没有使用mono_compile_aot,所以直接使用mono_domain_assembly_open打开程序集,执行mono_jit_exec。。。。具体的细节就不再写下去了,因为我发现这样写分析的方法没有人能看懂,我自己都懒得看。但是结果得说一下,通过这种方式我最终也没有找到再哪点输出了Hello World!把我记的笔记贴出来,换一种方法  

  1.  mono_main:driver.c   
  2.  main_thread_handler:driver.c   
  3.  mono_jit_exec :driver.c   
  4. mono_runtime_run_main:object.c   
  5.  mono_runtime_exec_main:object.c   
  6.  mono_runtime_invoke:mini.c   
  7.  runtime_invoke:指针。   
  8.  mono_custom_attrs_from_method:reflection.c,原来是取是否要线程安全的。   
  9.  mono_jit_compile_method_with_opt:mini.c   
  10. mono_jit_compile_method_inner:MONO_USE_AOT_COMPILER   
  11. mono_compile_create_vars这个创建私有作用域的变量吗?   
  12. mono_method_get_header获取_MonoMethodHeader是做什么的?   
  13.  mono_arch_create_vars指定特定arch的操作。   
  14.  mono_method_to_ir:mono_method_to_ir.c这个好生疏阿。Translate the .net IL into linear IR   
  15.  mono_method_to_ir是一个很重要的方法,在这里实现了il的解析。   
  16.  mono_optimize_branches:优化分支,开始执行代码优化了,编译理论书上写的没错。  

2.第二次尝试:找到在哪执行了向stdout上写hello world! 这一次直接一点,直接去找最终的执行函数,中间的无数步骤先忽略。最初我再源码里翻出了console-io.h这个头文件,发现这里有操作console的,然后就断点阿,每个相关的函数都断上,执行的***只有初始化函数命中了,尝试失败。那么到底再哪点往stdout写数据呢?我再次尝试一种方法,在执行时加上--trace选项(即再terminal里执行mono --trace hello.exe >> hello.trace.out),把执行流程重定向的一个文本文件中,输出了700多行,恩,我想从这里找出点线索。

1.首先在hello.trace.out文件里搜索Hello, World!字符串,找到几个匹配的地方,其中有一个是 [0xb75616f0: 0.02866 1] ENTER: System.Console:WriteLine (string)([STRING:0x53fa0:Hello, World!], ) 从这个trace的结果上可以看出,[]包括的是地址和时间,后面跟着ENTER:,于是我就再代码里搜索ENTER:,找到/mono/mini/trace.c的342行,加上条件断点:fname = "System.Console:WriteLine",执行一下,ok,命中断点,堆栈信息如下。 

  1. Thread [1] 8643 (Suspended : Breakpoint)   
  2.    mono_trace_enter_method() at trace.c:342 0x80dbe6c   
  3.   0xfb817c   
  4.    0xfb8225   
  5.    0xfb80f8   
  6.    mono_jit_runtime_invoke() at mini.c:4,789 0x8065cc1   
  7.   mono_runtime_invoke() at object.c:2,613 0x8195d35   
  8.   create_exception_two_strings() at exception.c:133 0x81e6a9f   
  9.  mono_exception_from_name_two_strings() at exception.c:156 0x81e6adf   
  10.  create_exceptions() at appdomain.c:185 0x818b9e4   
  11.   mono_runtime_init() at appdomain.c:262 0x818bc0d   
  12.  mini_init() at mini.c:5,520 0x806734a   
  13.   mono_main() at driver.c:1,623 0x80ec127   
  14.  main() at main.c:34 0x805b950  

再看看后面几个的调用

  1.  System.IO.SynchronizedWriter:WriteLine   
  2.  System.IO.TextWriter:WriteLine   
  3.  System.IO.UnexceptionalStreamWriter:Write   
  4.  System.IO.StreamWriter:Write   
  5. ......   
  6. System.IO.MonoIO:Write  

都是同样的调用堆栈,另外有关于这些System.Console相关的函数都在mono-2.6.7/mcs/class/corlib/System/Console.cs里,这个代码里有MonoLimitation和#if NET_2_1 && !MONOTOUCH部分符号,有点意思。

2.现在可以大致确定每个clr的方法调用都是由mono_jit_runtime_invoke发出的,但是我有点不解的是有三个函数没有symbol,直接显示的是函数地址,这给我的进一步分析带来了难度,来看看是为什么。执行函数返回退回到堆栈mono_jit_runtime_invoke,可以看到那几个没有符号信息的地址是由runtime_invoke这个函数执行的。转到定义,可见  

  1.  MonoObject *(*runtime_invoke) (MonoObject *thisvoid **params, MonoObject **exc, void* compiled_method);   
  2. ......   
  3. if (!info->dyn_call_info)   
  4.    info->runtime_invoke = mono_jit_compile_method (invoke);   
  5.  ......   
  6. runtime_invoke = info->runtime_invoke;   
  7. ......   
  8. return runtime_invoke (obj, params, exc, info->compiled_method);  

可见runtime_invoke是由mono_jit_compile_method这个方法进行jit生成的代码,怪不得找不到符号了。

3.继续深入到mono_jit_compile_method_with_opt这个动态代码生成的函数,关于jit方面的,留下次分析吧。

原文链接:http://www.cnblogs.com/zffl/archive/2011/03/13/1983007.html

【编辑推荐】

  1. 使用开源工具MonoDevelop开发GTK#图形界面
  2. 详解Mono 2.8上如何部署ASP.NET MVC 2框架
  3. 详解在Mono中如何创建ASP.NET程序
  4. Mono2.0发布 Linux下的.NET框架成熟吗?
  5. 讨论:Mono这只猴子招惹了谁? 
责任编辑:彭凡 来源: 博客园
相关推荐

2009-08-11 10:32:23

什么是Groovy

2012-08-27 09:10:05

JVMJava

2014-09-17 11:45:20

iOS编程App运作

2021-03-30 13:48:47

WebAssemblyWeb浏览器编程语言

2016-09-21 12:56:31

JavascriptWeb前端

2017-11-23 17:45:46

Yii框架IntelYii框架深度剖析

2014-12-19 10:07:10

C

2017-11-28 16:57:18

2009-07-30 13:21:17

Scala入门Hello World

2009-09-16 17:15:19

OSGi Bundle

2023-09-04 07:30:03

Wasm汇编语言

2011-06-08 14:39:06

Qt 教程

2023-01-06 08:18:44

2012-02-20 14:26:48

JavaPlay Framew

2023-05-23 08:01:10

Netty网络通信

2009-08-14 16:54:19

C# Hello Wo

2014-04-11 11:36:42

NDKAndroid开发终端

2011-08-05 09:48:46

iPhone Interface

2021-11-26 08:22:01

Java动态开发

2024-04-11 13:13:27

点赞
收藏

51CTO技术栈公众号