在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
介绍
注意:公共语言运行时CLR掌管每一个.NET应用程序的执行环境,它为运行托管应用程序提供环境和服务。CLR必须运行在Win32进程中,ASP.NET是.NET框架为CLR提供的一个宿主之一,确切地说在ASP.NET中,ASP.NET工作进程(IIS 5中为aspnet_wp.exe,IIS6中为w3wp.exe)就是运行CLR的进程。 在研究这些类中发生的交互行为的技术细节之前,我们先大致看一下处理请求时究竟发生了什么。我前面已经介绍过这两个类,因为处理请求的入口点可以大致的分为两个方面。 1. 如果AppDomain还不存在则创建APPDomain,将AppDomain指派给与请求对应的应用程序,这通过AppManagerAppDomain类实现。 2. 提交和处理请求,由ISAPIRuntime类实现。 这两个方面都很重要,第一个包括了不需要开发者参与的一些交互动作,主要涉及应用程序的运行环境,而第二个则是结构上最可配置的部分,我将在这个系列的后续文章中完整地探讨。 从另一个角度来看,第一步包含的操作在应用程序生存期里只执行一次,就是说在启动的时候,而第二步包含的交互在每一次定位请求的目标应用程序时都会出现。 创建AppDomain 在前面文章中了解到,一个ASP.NET应用程序被封装成一个叫做应用程序域的实体,简写AppDomain,由ASP.NET架构中的一个类AppDomain表示。当特定应用程序的请求到达时,如果不存在则必须创建AppDomain对象,这通常发生在属于特定应用程序的请求第一次到达时,或者由于某种原因相应的AppDomain被关闭掉了,几个可能导致这个问题的原因我在后面会谈到。注意一个ASP.NET应用程序只会有一个AppDomain存在,它跟IIS应用程序一对一映射,可能基于物理目录或者虚拟目录创建。那么这个类的实例是怎样创建的呢? 图1:创建AppDomain实例时由JetBrains dotTrace Profiler产生的调用栈 使用Reflector可以推断AppDomain有一个抛出NotSupportedException异常的私有构造器,因此很明显这个方法行不通。实际上初始化AppDomain实例的入口点是AppManagerAppDomainFactory.Create方法,为了找出这一点,需要使用一个分析器跟踪初始化对象时生成的调用栈。图1显示了JetBrains dotTracProfiler的截屏,它显示了运行在IIS上的一个web应用程序在初始化AppDomain时生成的调用栈。有很多类参与了这个过程,它们也许是开发者永远不需要用到的。 注意:AppManagerAppDomainFactory类在CLR初始化过程中只运行一次,就像它的名字暗示的,它作为创建AppDomain以及其他重要对象的入口点。 图2显示了一个跟前面图1一样的调用栈,这次是从微软CLR Profiler调用树的输出中截取的,它为AppDomain的初始化提供了更多的信息。实际上高亮行表明主线程创建了两个AppDomain类的实例。 图2:微软CLR Profiler调用树视图显示的创建AppDomain实例时的调用栈 那么是怎样创建这个额外的实例呢,为什么创建?很不幸这不是个容易的问题。前面图中显示的调用栈,指示了在创建与实际执行应用程序相关的AppDomain实例时采用的步骤,因此这个额外的实例,看起来像是用于某些难于理解的目的的辅助对象,尽我所能的猜测,它被用于容纳所有不属于任何应用程序的对象,例如AppManagerAppDomainFactory,因此被放置在一个隔离的AppDomain中。对于AppManagerAppDomainFactory类,这个辅助AppDomain只在CLR初始化时实例化一次,它在CLR初始化很早的时候进行,如图3所示。 图3:额外AppDomain实例的顺序和分配方法 图3展示了Red Gate ANTSProfiler的一个截屏,它显示了额外AppDomain实例在CLR初始化很早的时候创建,很明显它比AppManagerAppDomainFactory类更早创建,因为很有可能它是AppManagerAppDomainFactory的容器。实际上在前面分析会话图示中,单例AppManagerAppDomainFactory实例的ID为52。 另一个查看前面提到的两个AppDomain实例初始化过程的方法,是微软CLR Profiler提供的分配视图,它创建一个图形流程,展示处理中类的使用位置,像图4显示那样。 图4:微软CLR Profiler分配视图显示的两个AppDomain初始化过程 它展示了一个平面视图,从根元素,即CLR,和线程类之间裁剪出所有的类,然而它清晰的显示了创建两个AppDomain时的调用顺序。 从前面图中可以得到另外一个关于AppDomain实例的信息是,每个实例占用100字节内存。这不会带来多少问题,但有人会认为AppDomain是一个很大的类,因为它必须容纳整个应用程序。实际上它提供很多的服务,但并不存储太多数据。 到现在辅助AppDomain实例已经被创建了,包括AppManagerAppDomainFactory类,它的Create方法就是图1和2中显示的调用栈的入口方法。 这个方法通过COM暴露出来,提供给调用者,因此是非托管代码。AppManagerAppDomainFactory类实现了 IAppManagerAppDomainFactory接口,它的结构显示在图5中。 图5:IAppManagerAppDomainFactory接口结构 这个接口使用ComImport和InterfaceType属性修饰,它们将类型绑定到非托管接口类型上。调用这个方法时,将产生图1和2的调用栈图形,最终触发AppDomain类实例的创建,容纳用于处理请求的目标应用程序。 AppDomain对象创建以及运行之后,剩余工作就是处理请求,这比前面已经完成的工作内容更多,这也许是ASP.NET架构中最有意思的部分,但在这个有趣的部分开始之前我已经介绍了一些关键的主题。 总结 这篇文章中我介绍了ASP.NET基础结构中一些很底层的主题,涉及那些由ASP.NETISAPI扩展展示的非托管世界,以及运行web应用程序的AppDomain所展示的托管世界之间的一些接口。我展示了初始化AppDomain的机制,以及哪些类参与了这个处理。下一篇文章中我会通过HTTP管道讨论托管代码,在我的映像里这应当是ASP.NET架构最吸引人的一章,实际上它使得ASP.NET不同于其他所有的web开发框架。我希望你像我写这篇文章时一样充满兴趣的阅读,我的建议是打开一些分析工具自己尝试这些内容,这是充分理解它怎样工作的最好方法。 |
请发表评论