因由
结束了一年多的出差,回到了公司上班,最近一直在想着博客的改版,连博客也没怎么写了,正好这两天不怎么忙,便想起看看mono的源码,至于为什么看mono源码而不是选择.Net Core源码,Mono源码是纯c的,看起来更容易一些,不需要c++的知识.实在.Net Core源码直接用VS调试还是很方便的.不过source insight对c++支持的不是很好.阅读Mono源码使用source insight还是很方便的.
在Github看Mono已经把zlib源码加入到Mono源码,在编译源码的时候,不用手动链接zlib静态库了
阅读源码从mini/main.c开始.
函数名以g_开头的,是eglib封装的工具函数.重要用于简化操作.如g_new0/g_free
屏蔽系统API差别
这里以mono_pagesize为例.先认识Windows的源码.
//获取系统页巨细int mono_pagesize(void){ SYSTEM_INFO info; static int saved_pagesize = 0; //saved_pagesize 使用static声明 if (saved_pagesize) return saved_pagesize; //第二次调用mono_pagesize函数,直接返回saved_pagesize GetSystemInfo(&info); //第一次调用mono_pagesize函数,调用GetSystemInfo系统函数 saved_pagesize = info.dwPageSize; //将获取到系统页巨细赋值给saved_pagesize return saved_pagesize;}Linux源码:
int mono_pagesize (void){ static int saved_pagesize = 0; if (saved_pagesize) return saved_pagesize; // Prefer sysconf () as it's signal safe.#if defined (HAVE_SYSCONF) && defined (_SC_PAGESIZE) //判定是否有sysconf系统函数 saved_pagesize = sysconf (_SC_PAGESIZE); #else saved_pagesize = getpagesize (); //不支持sysconf系统函数#endif return saved_pagesize;}根据宏判定是否支持sysconf系统调用.
Windows和Linux 系统页巨细默以为4096
实在Windows默认的页巨细也是4096,这里没有截取Windows系统页巨细.
mono_main 主函数
初学mono源码,mono加载过程
main.c int main(void) //Windows 通过CommandLineToArgvW获取参数 int main(int argc, char* argv[]) //Linux或Unix及Mac int mono_main_with_options(argc, argv)driver.c //主函数 int mono_main(argc, argv) coree.c //加载mscoree.dll,Windows独有的, c# exe(Windows下)实验的时候通过ntdll->mscoree.dll->mscorlib.dll void mono_load_coree(const char* exe_file_name) mono-config.c //设置lib和etc,具体可以看 https://www.qiufengblog.com/articles/mono-config.html void mono_config_parse (const char *filename) mini-runtime.c //mono CLR运行时初始化,返回MonoDomain MonoDomain* mini_init(const char* filename, const char* runtime_version) dirver.c static void main_thread_handler (gpointer user_data)domain.c //根据domain,加载程序集(并没有实验) MonoAssembly* mono_domain_assembly_open(MonoDomain* domain, const char* name)driver.c //根据domain,实验程序集 int mono_jit_exec (MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[])mini-runtime.c //清理domain,释放资源 void mini_cleanup(MonoDomain* domain)简化之后的代码:
//函数原型MonoDomain* mini_init(const char* filename, const char* runtime_version);//mono 主函数//函数去除很多代码int mono_main(int argc, char* argv[]){ // //去除剖析参数处理,背面用到单独阐明 // MonoThreadArgs main_args; int i; int mixed_mode = FALSE; if (mixed_mode) { mono_load_coree(argv); } mono_config_parse(config_file); mono_set_defaults(mini_verbose_level, opt); MonoDomain* domain = mini_init(argv, forced_version); main_args.domain = domain; main_args.file = aname; main_args.argc = argc - i; main_args.argv = argv + i; main_args.opts = opt; main_args.aot_options = aot_options; main_thread_handler(&main_args); mono_thread_manage(); mini_cleanup(domain); i = mono_environment_exitcode_get(); return i;}MonoDomain* mini_init(const char* filename, const char* runtime_version){ //1.初始化很多东西 //2.注册回调函数 MonoDomain* domain; //通过mono program.exe 没有指定--runtime=v4.0.30319 ,runtime_version不为真的 if (runtime_version) { domain = mono_init_version(filename, runtime_version); } else { domain = mono_init_from_assembly(filename, filename); } //还要处理很多东西 //xxx //xxx return domain;}在fixed模式下,mono如何加载mscoree.dll
基于mono_load_coree函数源码,改动而来,重要是动手练习一下.也顺便一些系统函数的调用.
typedef unsigned __int16 gunichar;//1. 根据GetSystemDirectory系统函数,获取系统目次//2. 将获取到系统目次和mscoree.dll进行拼接//3. 通过LoadLibrary加载到内存HMODULE load_coree(){ UINT len = GetSystemDirectory(NULL, 0); printf("%d\n", len); printf("unsigned __int16 size:%d\n", sizeof(gunichar)); gunichar* dir = calloc(1, (len + 12) * sizeof(gunichar)); GetSystemDirectory(dir, len); //打印系统路径 printf("system dir:%ls\n", dir); if (dir[len - 1] != L'\\') { dir[len - 1] = L'\\'; } //拼接dll路径 memcpy(&dir[len], L"mscoree.dll", 12 * sizeof(gunichar)); printf("dir :%ls\n", dir); //LoadLibrary (将dll加载内存中,将文件的起始位置返回) //LoadLibrary和FreeLibrary 配对使用, HMODULE module = LoadLibraryW(dir); return module;}//验证该句柄是否位pe文件的起始位置int validate_pe(HMODULE hModule){ PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)hModule; DWORD magic = dos_header->e_magic; char* a = (char*)&magic; printf("%s\n", a); if (magic == IMAGE_DOS_SIGNATURE) { printf("pe dos header Signature:MZ\n"); } PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((char*)dos_header + dos_header->e_lfanew); if (nt_header->Signature == IMAGE_NT_SIGNATURE) { printf("pe dos header Signature:PE\n"); } return 0;}要不是看了源码,怎么也没想到LoadLibrary返回的HMODULE,竟然是文件在内存中的起始地址.
这是2019年所写,不是学Mono写的第一篇,只是Mono代码一直改进,如今Mono源码已经和.Net源码放在一起了,今天本想在本地用VS编译Mono的,编译失败了,如今编译Mono源码需要使用CMake天生VS解决方案.
在2021年,Mono侧重于移动端和Blazor(WebAssmebly), .Net Core侧重于服务端(说Web也不符合,由于Blazor客户端是用Mono) |