挡不住的好奇心:ASP.NET 5是如何通过XRE实现跨平台的

.NET程序员也有自己的幸福,.NET的跨平台是一种幸福,.NET的开源也是一种幸福,而更幸福的是可以通过开源的.NET了解.NET是如何一步步走向跨平台的,所以幸福是一种过程。
在.NET跨平台的进程中,ASP.NET显然走在了前头,而通过探究ASP.NET 5是如何实现跨平台的,可以稍稍满足一下自己的好奇心。
体验ASP.NET 5跨平台有2种方式:
1)在Mac下,git签出XRE的源代码(前身是KRuntime),然后运行sh build.sh,就能完成整个XRE项目的生成。
2)在Mac下,写一个简单的ASP.NET项目,然后用k kestrel运行,详见不写1行代码,在Mac上体验ASP.NET 5的最简单方法。
这篇博文就从k命令下手,一探ASP.NET 5跨平台的究竟。
运行k kestrel(即将是dotnet kestrel),实际运行的是下面的命令(根据project.json中的commands配置):
k "Microsoft.AspNet.Hosting --server kestrel --server.urls http://localhost:8002"
Microsoft.AspNet.Hosting是一个.NET控制台程序实现的OWIN Host(源码),kestrel是一个基于libuv用.NET实现的OWIN Server(也是Web Server,源码),kestel是由Microsoft.AspNet.Hosting加载的。
既然Microsoft.AspNet.Hosting是一个托管程序,它自己是无法直接运行的。因为运行一个.NET程序的前提条件是CLR已运行,而CLR自己不能运行自己,CLR运行的前提是有一个host程序将它加载。
如果你在Mac下用过Mono,就你就知道运行一个.NET程序需要用mono命令,mono命令的作用就是创建一个进程,加载Mono Runtime(Mono CLR),然后由Mono Runtime执行.NET程序。
而在ASP.NET 5中,并没有直接用mono命令,而是k命令,自从KRuntime改名为XRE之后,k命令也将会被dotnet命令取代。

在非Windows平台下,k命令对应的是k.sh。现在改为XRE之后,也就是donet命令对应dotnet.sh。所以ASP.NET 5跨平台的秘密就藏在dotnet.sh中。
下面就直击XRE项目中的scripts/dotnet.sh:
#...
if [ -f "$DIR/mono" ]; then
exec "$DIR/mono" $MONO_OPTIONS "$DIR/dotnet.mono.managed.dll" "$@"
else
exec mono $MONO_OPTIONS "$DIR/dotnet.mono.managed.dll" "$@"
fi
毫无悬念,依然用的是mono。但是用了mono,如何加载.NET Core CLR,难道还是用Mono Runtime?
带着这个疑问,顺藤摸瓜,看dotnet.mono.managed.dll干了啥。
dotnet.mono.managed.dll的实现代码就在XRE项目中,是一个简单的C#控制台程序,它干了两件事:1)分析命令行参数;2)调用RuntimeBootstrapper.Execute():
public class EntryPoint
{
public static int Main(string[] arguments)
{
//...
arguments = ExpandCommandLineArguments(arguments);
//...
return RuntimeBootstrapper.Execute(arguments);
}
}
继续顺藤摸瓜至 [dotnet.hosting.RuntimeBootstrapper],[RuntimeBootstrapper.Execute()] 调用了 [RuntimeBootstrapper.ExecuteAsync()]。
而托管的 [RuntimeBootstrapper.ExecuteAsync()] 竟然拐了个弯,执行了非托管的dotnet命令(一个由dotnet.cpp实现的C++程序)。
mono命令(非托管) -> Mono Runtime -> dotnet.mono.managed(托管) -> RuntimeBootstrapper(托管) -> dotnet命令(非托管),ASP.NET 5 XRE的代码真是十八弯。

千呼万唤始出来,原来真正的主角就藏在十八弯之后。
dotnet.cpp加载了非托管的dotnet.coreclr.dll:
LPCWSTR pwzHostModuleName = L"dotnet.coreclr.dll";
m_hHostModule = ::LoadLibraryExW(pwzHostModuleName, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
pfnCallApplicationMain = (FnCallApplicationMain)::GetProcAddress(m_hHostModule, pszCallApplicationMainName);
而dotnet.coreclr.dll是由XRE中的C++程序dotnet.coreclr.cpp实现的,最终由dotnet.coreclr.cpp加载了coreclr.dll:
hCoreCLRModule = ::LoadLibraryExW(L"coreclr.dll", NULL, );
dotnet.coreclr.cpp就是加载CLR的主角。
这不让人产生疑问,这也可以?仅靠一个C++程序就能加载CLR,执行.NET程序,那我们在Windows上为什么要安装一个庞大的.NET Framework?
真的可以!一个非托管的host程序+CLR,就能运行.NET程序,不信你可以看这篇文章震撼一下:Hosting .NET Core Clr in your own process 。
加载CLR的目的是为了执行.NET程序集中的IL代码,而要执行的程序集是由dotnet命令(前身是k命令)的命令行参数所传递过来的,比如dotnet kestrel(之前是k kestrel),对应的程序集是 Microsoft.AspNet.Hosting。CLR调用 Microsoft.AspNet.Hosting.Program.Main() 方法开始执行,ASP.NET 5就开始干活了。
Core CLR被加载、Microsoft.AspNet.Hosting被运行之后,在 RuntimeBootstrapper.ExecuteAsync() 中,还继续加载了一些dotnet.host的相关程序集(注意:这时不是Core CLR,而是Mono Runtime)。
//...
var assembly = Assembly.Load(new AssemblyName("dotnet.host"));
//...
var loaderContainerType = assembly.GetType("dotnet.host.LoaderContainer");
var cachedAssemblyLoaderType = assembly.GetType("dotnet.host.CachedAssemblyLoader");
var pathBasedLoaderType = assembly.GetType("dotnet.host.PathBasedAssemblyLoader");
//...
到这里,不知你有没有被这十八弯给绕晕,如果没被绕晕,请继续往下看。

这时一个大大的问号浮现在眼前,既然dotnet命令能直接加载Core CLR,为什么还要用mono命令中转一下?
百思不得其解。。。
在写这篇博文的过程中,突然产生了一个大胆猜想——
在Core CLR被加载,Microsoft.AspNet.Hosting被执行之后,为什么还要用Mono Runtime加载一些dotnet.host相关的程序集?为什么不直接用Core CLR加载呢?这只能用一个原因来解释,dotnet.host依赖的一些程序集在在.NET Framework中有实现,但是在.NET Core Framework中还没有实现,而Mono是.NET Framework的一个跨平台实现,在Mono中也有对应的实现。完整的.NET Core Framework(github.com/dotnet/corefx)还在紧张开发之中,在它出来之前,微软只能借助Mono。这也是ASP.NET的跨平台走在前面要付出的代价,随着.NET Core Framework的完成,XRE的改进,可以预计ASP.NET的跨平台是会脱离Mono的。
当然,这只是一个猜想,如果你知道真相,欢迎来揭开。
挡不住的好奇心:ASP.NET 5是如何通过XRE实现跨平台的的更多相关文章
- ASP.NET 5是如何通过XRE实现跨平台的
挡不住的好奇心:ASP.NET 5是如何通过XRE实现跨平台的 .NET程序员也有自己的幸福,.NET的跨平台是一种幸福,.NET的开源也是一种幸福,而更幸福的是可以通过开源的.NET了解.NET ...
- java中的finally用return也挡不住
今晚做了科达的题,有一题就是这个意思,我自以为return中断一切,然而事实摆在眼前:
- linux 再多的running也挡不住锁
再续<linux 3.10 一次softlock排查>,看运行态进程数量之多: crash> mach MACHINE TYPE: x86_64 MEMORY SIZE: GB CP ...
- 本科毕业平均年薪 30 万!经济寒冬挡不住 AI 人才的火热!
互联网行业遭遇寒冬,企业纷纷裁员缩招,而 BAT 和硅谷明星公司对 AI 人才的投入却并不见放缓.为争夺相关人才,给应届毕业生开出的平均年薪高达 30 万. 而 TensorFlow 作为当下最流行的 ...
- .NET:从 Mono、.NET Core 说起
魅力 .NET:从 Mono..NET Core 说起 前段时间,被问了这样一个问题:.NET 应用程序是怎么运行的? 当时大概愣了好久,好像也没说出个所以然,得到的回复是:这是 .NET 程序员最基 ...
- 魅力 .NET:从 Mono、.NET Core[转]
前段时间,被问了这样一个问题:.NET 应用程序是怎么运行的? 当时大概愣了好久,好像也没说出个所以然,得到的回复是:这是 .NET 程序员最基本的...呵呵! 微软开源,其实不只是对 .NET 本身 ...
- 在网络7层协议中,如果想使用UDP协议达到TCP协议的效果,可以在哪层做文章?(QQ 为什么采用 UDP 协议,而不采用 TCP 协议实现?)
为了解决这题,可以具体看看下面这个讨论. 解灵运工程师 185 人赞同 某次架构师大会上那个58同城做即时通信的人说:原因是因为当时没有epoll这种可以支持成千上万tcp并发连接的技术,所以他们使用 ...
- 基于阿里云容器服务用docker容器运行ASP.NET 5示例程序
小试阿里云容器服务 之后,接下来有一个挡不住的小试冲动--用docker容器运行程序.首先想到的程序是 ASP.NET 5示例程序,于是参考msdn博客中的这篇博文 Running ASP.NET 5 ...
- ASP.NET Core 介绍
原文:Introduction to ASP.NET Core 作者:Daniel Roth.Rick Anderson.Shaun Luttin 翻译:江振宇(Kerry Jiang) 校对:许登洋 ...
随机推荐
- 上传文件 隐藏input type="file",用text显示
<div> <span>上传文件:</span> <input type="file" id="upload_file" ...
- 【Java EE 学习 79 上】【mybatis 基本使用方法】
一.简介 mybatis类似于hibernate,都是简化对数据库操作的框架,但是和hibernate不同的是,mybatis更加灵活,整体来说框架更小,这体现在它需要我们手写SQL语句,而hiber ...
- dbca建库sys用户被锁
奇怪问题:dbca建库sys用户被锁, 点击密码管理报账户被锁 而且在服务器上无法进行操作系统验证登陆,经过一番检查发现oracle用户和grid用户没有在dba组里 解决: 1.把oracle用户和 ...
- Javascript实现的数组降维——维度不同,怎么谈恋爱
数组的元素可能是数组,这样一层层嵌套,可能得到一个嵌套很深的数组,数组降维要做的事就是把嵌套很深的数组展开,一般最后得到一个一维数组,其中的元素都是非数组元素,比如数组[1, [2, 3, [4, 5 ...
- ExtJS客户端代理
代理(proxy)分为两大类:客户端代理和服务器端代理.客户端代理主要完成与浏览器本地存取数据相关的工作,服务器端代理则是通过发送请求,从服务器端获取数据.根据各自获取数据的方式,客户端代理和服务器端 ...
- 修改cmd的字体为Consolas字体
Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe] ...
- 安装HBase
安装HBase 1.默认已经安装好java+hadoop+zookeeper 2.下载对应版本的HBase 3.解压安装包 tar zxvf hbase-1.0.2-bin.tar.gz 4.配置环境 ...
- 常用的sublime text插件(很爽哦)
个人比较懒,平时喜欢用webstorm,但是因为webstorm打开实在太慢了,并且太看设备,所以本人编辑简单的文件依然会选择使用sublime,虽然网上有很多关于此类插件的分享了,但是感觉都是片段, ...
- 调用WCF不需要添加服务引用,使用一个WCFHelper类就可以
效果图: 调用过程: string WCFURL = "http://localhost:100/Service1.svc"; UserRequest user = new Use ...
- HTTP和HTTPS
HTTP和HTTPS HTTP协议通常承载于TCP协议之上,在HTTP和TCP之间添加一个安全协议层(SSL或TSL),这个时候,就成了我们常说的HTTPS. 默认HTTP的端口号为80,HTTPS的 ...