.NET程序员也有自己的幸福,.NET跨平台是一种幸福,.NET开源也是一种幸福,什么是更开心的是,通过.NET的开源,一步步了解到.NET是如何跨平台的,所以快乐是一个过程。在.NET的跨平台进程中,ASP.NET显然走在了前列,而通过探究ASP.NET5是如何实现跨平台的,可以稍微满足一下你的好奇心。体验ASP.NET5跨平台有两种方式:1)在Mac下,用git查看XRE(原KRuntime)的源码,然后运行shbuild.sh完成整个XRE工程的生成。2)Mac下,写一个简单的ASP.NET工程,然后用kkestrel运行。有关详细信息,请参阅无需编写一行代码即可在Mac上体验ASP.NET5的最简单方法。这篇博文从k命令开始,探讨ASP.NET5的跨平台特性。运行kkestrel(即将成为dotnetkestrel),实际运行的是如下命令(根据project.json中的commands配置):k"Microsoft.AspNet.Hosting--serverkestrel--server.urlshttp://localhost:8002"Microsoft.AspNet.Hosting是一个OWINHost(源代码),由.NET控制台程序实现,kestrel是一个OWIN基于libuv的.NET实现的Server(也是WebServer,源码),kestel是通过加载Microsoft.AspNet.Hosting实现的。由于Microsoft.AspNet.Hosting是一个托管程序,它本身不能直接运行。因为运行.NET程序的前提条件是CLR已经在运行,而CLR不能自己运行。CLR运行的前提是有宿主程序加载它。如果你在Mac下使用过Mono,你就知道你需要使用mono命令来运行一个.NET程序。mono命令的作用是创建一个进程,加载MonoRuntime(MonoCLR),然后由MonoRuntime执行.NET程序。在ASP.NET5中,不再直接使用mono命令,而是使用k命令。由于KRuntime更名为XRE,因此k命令也将被dotnet命令取代。在非Windows平台上,k命令对应于k.sh。现在改成XRE后,donet命令对应的是dotnet.sh。所以ASP.NET5跨平台的秘密就藏在dotnet.sh中。现在只需在XRE项目中点击scripts/dotnet.sh:#...if[-f"$DIR/mono"];然后exec"$DIR/mono"$MONO_OPTIONS"$DIR/dotnet.mono.managed.dll""$@"elseexecmono$MONO_OPTIONS"$DIR/dotnet.mono.managed.dll""$@"fi没有悬念,还是用单声道。但是有了mono,怎么加载.NETCoreCLR,还是用MonoRuntime吗?带着这个疑问,顺藤摸瓜看看dotnet.mono.managed.dll是干什么的。dotnet.mono.managed.dll的实现代码在XRE项目中。这是一个简单的C#控制台程序。它做了两件事:1)分析命令行参数;2)调用RuntimeBootstrapper.Execute():publicclassEntryPoint{publicstaticintMain(string[]arguments){//...arguments=ExpandCommandLineArguments(arguments);//...返回RuntimeBootstrapper.Execute(arguments);}}继续顺藤摸瓜[dotnet.hosting.RuntimeBootstrapper],[RuntimeBootstrapper.Execute()]调用[RuntimeBootstrapper.ExecuteAsync()]。托管的[RuntimeBootstrapper.ExecuteAsync()]拐了个弯,执行了一个非托管的dotnet命令(由dotnet.cpp实现的C++程序)。Monocommand(unmanaged)->MonoRuntime->dotnet.mono.managed(managed)->RuntimeBootstrapper(managed)->dotnetcommand(unmanaged),ASP.NET5XRE的代码真是十八折。千呼万唤始出来,原来真正的主角就藏在18号弯的后面。dotnet.cpp加载非托管dotnet.coreclr.dll:LPCWSTRpwzHostModuleName=L"dotnet.coreclr.dll";m_hHostModule=::LoadLibraryExW(pwzHostModuleName,NULL,LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);pfnCallApplicationMain=(Modh::Prom_ApplicationAddcMains)pszCallApplicationMainName);而dotnet.coreclr.dll是由XRE中的C++程序dotnet.coreclr.cpp实现的,最后由dotnet.coreclr.cpp加载coreclr.dll:hCoreCLRModule=::LoadLibraryExW(L"coreclr.dll",NULL,0);dotnet.coreclr.cpp是加载CLR的主角。这不会引起怀疑,这样可以吗?只有一个C++程序可以加载CLR和执行.NET程序,那么为什么我们要在Windows上安装一个庞大的.NETFramework呢?真的可以!非托管宿主程序+CLR可以运行.NET程序。如果你不相信我,你可以阅读这篇文章并感到震惊:在你自己的进程中托管.NETCoreClr。加载CLR的目的是执行.NET程序集中的IL代码,要执行的程序集通过dotnet命令(原k命令)的命令行参数传递,如dotnetkestrel(原kkestrel),对应的程序集是Microsoft.AspNet.Hosting。CLR调用Microsoft.AspNet.Hosting.Program.Main()方法开始执行,ASP.NET5开始工作。CoreCLR加载完毕,运行Microsoft.AspNet.Hosting后,在RuntimeBootstrapper.ExecuteAsync()中,继续加载dotnet.host的一些相关程序集(注意:此时不是CoreCLR,而是MonoRuntime)。//...varassembly=Assembly.Load(newAssemblyName("dotnet.host"));//...varloaderContainerType=assembly.GetType("dotnet.host.LoaderContainer");varcachedAssemblyLoaderType=assembly.GetType("dotnet.host.CachedAssemblyLoader");varpathBasedLoaderType=assembly.GetType("dotnet.host.PathBasedAssemblyLoader");//...说到这里,不知你有没有被这十八般拐弯,如有不解,请指教继续阅读。这时,我的眼前出现了一个大大的问号。既然dotnet命令可以直接加载CoreCLR,为什么还要用mono命令来调用呢?困惑。..在写这篇博文的过程中,我突然有了一个大胆的猜测——CoreCLR加载完成,Microsoft.AspNet.Hosting执行完毕后,为什么要用MonoRuntime加载一些dotnet.host相关的程序集呢?为什么不直接用CoreCLR加载它呢?这只能用一个原因来解释。dotnet.host依赖的一些程序集在.NETFramework中实现,但在.NETCoreFramework中尚未实现,Mono是.NETFramework的跨平台实现。Mono中也有相应的实现。完整的.NETCoreFramework(github.com/dotnet/corefx)仍在紧张开发中。在它出来之前,微软只能依赖Mono。这也是ASP.NET跨平台要付出的代价。随着.NETCoreFramework的完成和XRE的完善,ASP.NET跨平台将脱离Mono是可以期待的。当然,这只是一个猜想,如果你知道真相,欢迎揭秘。
