精彩专题推荐:建站之入门课 建站之必修课 建站之关键课 网站价值所在 流量提高专题 css+div 标准 个人网站打造全过程
返回建站学首页
导航:
建站首页 | 网站设计 | 网站开发 | 网站运营 | 网页软件 | 建站指南 | 搜索优化 | 图像处理 | 视频教程 | 书籍教程 | 建站专题
当前位置:首页>网站开发>asp.net教程>正文

从.NET类库代码来看ASP.NET运行时


来源:不详 时间:07-08-24 点击: 点击这里收藏本文

  好,现在我们知道了ISAPIRuntime对象是托管代码的入口点,那么这个对象是什么时候产生的呢?换句话说,w3wp也是一个非瀀?潳楬?托管代码写出的程序,它是在什么时候把.net运行时加载进来的呢?(如果好奇心再强一点,还可以问一问一个工作进程是什么时刻产生并开始运行的,它和应用程序池有着怎样的交互。)完全解释清楚这些问题已经超过了笔者目前的能力范围,还望高人补充或提供资料线索。但目前我们从.net的代码中应该可以推断出,ISAPIRuntime对象和应用程序域是对应的,.net在创建应用程序域的时候,就会创建ISAPIRuntime对象,见下面的创建应用程序域的代码:

  创建应用程序域

1/**//*这是System.Web.Hosting.AppDomainFactory类型的Create方法,它调用的是实际工厂的Create方法。*/
2[return: MarshalAs(UnmanagedType.Interface)]
3public object Create(string module, string typeName, string appId, string appPath, string strUrlOfAppOrigin, int iZone)
4{
5 /**//*实际工厂是一个AppManagerAppDomainFactory类型的对象。*/
6 return this._realFactory.Create(appId, appPath);
7}
8
9/**//*AppManagerAppDomainFactory.Create方法,请看代码内的注释。*/
10[return: MarshalAs(UnmanagedType.Interface)]
11public object Create(string appId, string appPath)
12{
13 object obj2;
14 try
15 {
16 if (appPath[0] == '.')
17 {
18 FileInfo info = new FileInfo(appPath);
19 appPath = info.FullName;
20 }
21 if (!StringUtil.StringEndsWith(appPath, '\\'))
22 {
23 appPath = appPath + @"\";
24 }
25 ISAPIApplicationHost appHost = new ISAPIApplicationHost(appId, appPath, false);
26 /**//*这个方法内部的调用链非常复杂,它一方面创建了一个应用程序域,一方面返回一个ISAPIRuntime对象。具体这个方法究竟是如何创建AppDomain对象的,大家可以用
27 JetBrain来跟踪其调用栈。关于这部分内容更详尽的信息,可参见ASP.NET Internals - The bridge between ISAPI and Application Domains一文。
28 另外,如果您使用JetBrain来调试系统程序集的话,有可能会因为缺少相应pdb文件而不能查看完整调试信息,这里提供一个根据已有程序集,先反汇编成中间码,
29 再重新以调试模式生成dll和pdb文件的方法:
30   1)生成IL文件: ildasm /tok /byt system.web.dll /out=system.web.il
31 2)重新生成PDB/DLL: ilasm system.web.il /DEBUG /DLL /OUTPUT=System.Web.dll*/
32 ISAPIRuntime o = (ISAPIRuntime) this._appManager.CreateObjectInternal(appId, typeof(ISAPIRuntime), appHost, false, null);
33 o.SetThisAppDomainsIsapiAppId(appId);
34 o.StartProcessing();
35 obj2 = new ObjectHandle(o);
36 }
37 catch (Exception)
38 {
39 throw;
40 }
41 return obj2;
42}

  三.Asp.net运行时,我们等待已久的纯托管代码环境

  好,经过上面长久的铺垫,我们终于进入了托管代码的领域。经过前面的内容,我们知道,在托管代码中首先被执行的是一个ISAPIRuntime对象的ProcessRequest方法,那么下面我们就来看一看这个方法主要做了些什么:

1/**//*ISAPIRuntime的方法,处理请求的入口。*/
2public int ProcessRequest(IntPtr ecb, int iWRType)
3{
4 try
5 {
6 /**//*这里ecb被作为参数传入,返回一个HttpWorkerRequest类型的对象,作为对一个请求的数据的封装。但HttpWorkerRequest
7 *只是一个抽象基类,CreateWorkerRequest作为一个工厂方法,返回的实际类型是ISAPIWorkerRequestInProc,
8 *ISAPIWorkerRequestInProcForIIS6或ISAPIWorkerRequestOutOfProc。这些类型里面提供的方法,其实大多
9 *围绕着如何从ecb中去获取数据,所以都包含了很多对System.Web.UnsafeNativeMethods类型中静态方法的调用。
10 **/
11 HttpWorkerRequest wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, iWRType);
12 string appPathTranslated = wr.GetAppPathTranslated();
13 string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
14 if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
15 {
16 /**//*从这里开始,对请求的处理流程就交给了HttpRuntime。需要注意的是,ISAPI是多线程的,而且对ProcessRequest的调用是异步的,
17 *这就要求HttpRuntime.ProcessRequest方法是线程安全的。看一看HttpRuntime.ProcessRequestNoDemand里的代码大家就清楚,
18 *所有的请求会被排成一个队列,顺次执行,保证了并发安全。
19 *最终,HttpRuntime.ProcessRequestInternal方法会被调用,我们接下来就去看看那个方法。
20 **/
21 HttpRuntime.ProcessRequestNoDemand(wr);
22 return 0;
23 }
24 HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[] { appDomainAppPathInternal, appPathTranslated }));
25 }
26 catch (Exception exception)
27 {
28 Misc.ReportUnhandledException(exception, new string[] { SR.GetString("Failed_to_process_request") });
29 throw;
30 }
31 return 1;
32}

  上面的代码段最主要的作用就是调用了HttpRumtime.ProcessRequestInternal方法,下面我们就一起来看看这个方法的实现:

1/**//*在HttpRuntime.ProcessRequestInternal()方法里,有如下几个重要的对象被创建出来:
2 *(1)HttpContext(包括其中的HttpRequest,HttpResponse)
3 *(2)HttpApplication
4 *同时,会执行HttpApplication对象的ProcessRequest方法,
5 */
6private void ProcessRequestInternal(HttpWorkerRequest wr)
7{
8 /**//*HttpContext对象在这里被创建。HttpWorkerRequest做为构造参数,而HttpWorkerRequest本身
9 *又围绕着对ecb的处理建立了一群高层的方法,它的实例会被HttpContext传给HttpRequest和HttpResponese
10 *做为他们的构造参数。所以,这里也能更清楚地看出HttpWorkerRequest作为ecb的托管环境封装器的实质。
11 *另外,这里也能清楚地反映出,每一个请求都有一个自己的HttpContext对象(而每一个Htt瀀?潳楬?pContext对象都管理着
12 *一个HttpSession对象--参见HttpContext的Session属性,这也就保证了每个访问者有自己的session对象。),你可以
13 *使用HttpContext.Current来访问到这个对象。
14 */
15 HttpContext extraData = new HttpContext(wr, false);
16 wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, extraData);
17 Interlocked.Increment(ref this._activeRequestCount);
18 HostingEnvironment.IncrementBusyCount();
19 try
20 {
21 try
22 {
23 this.EnsureFirstRequestInit(extraData);
24 }
25 catch
26 {
27 if (!extraData.Request.IsDebuggingRequest)
28 {
29 throw;
30 }
31 }
32 extraData.Response.InitResponseWriter();
33 /**//*用应用程序工厂返回一个HttpApplication对象。
34 *和线程池对线程的管理相似,HttpApplicationFactory中以stack维护了一个HttpApplication的列表(参见HttpApplicationFactory
35 *的_freeList变量)。在这句方法调用的最后,实际是调用了 _theApplicationFactory.GetNormalApplicationInstance(context),
36 *里面就是从_freeList的栈顶pop出一个已经构造的HttpApplication实例。
37 *所以,对于每一个请求,由HttpContext作为上下文,由一个HttpApplication对象来控制整个应用处理的pipeline,整个
38 *处理过程是在由工作进程管理的线程池中的某个线程内完成的。
39 *另外,在一个应用程序域内,由于可以同时处理多个请求,所以就有多个HttpApplication实例和多个活动线程(您可以使用windbg的sos
40 *扩展来观察它们之间的关系,本文就不继续深入了)。
41 *还有,对所有HttpModules的加载就是发生在HttpApplication对象的创建过程之中(包括系统已经提供的Authentication等模块儿和我们
42 *的自定义模块)。我们可以在Web.config里声明自己的自定义模块。这些模块的作用就是在整个HttpApplication处理管线的相关事件点上,
43 *挂上自己的处理。注意一下IHttpModule接口的Init()方法的声明,这个方法的传入参数就是要被创建的HttpApplication对象,所以,如果
44 *你自己的模块想在缓存读取上加入一些自定义操作,你只需进行如下处理即可:
45 public class YourCustomModule : IHttpModule
46 {
47 public void Init(HttpApplication application)
48 {
49 application.ResolveRequestCache += new EventHandler(this.YourCustomResolveRequestCache);
50 }
51 }
52 *另外,通过对HttpApplicationFactory.GetApplicationInstance方法内部实现方式的阅读,你会发现在每一个HttpApplication对象被创建
53 *之后,会立刻调用这个对象的InitInternal方法,而这个方法里面做了很多重要的初始化操作,内容较多,我们将在下文中单独介绍。
54 *
55 */
56 IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(extraData);
57 if (applicationInstance == null)
58 {
59 throw new HttpException(SR.GetString("Unable_create_app_object"));
60 }
61 if (EtwTrace.IsTraceEnabled(5, 1))
62 {
63 EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, extraData.WorkerRequest, applicationInstance.GetType().FullName, "Start");
64 }
65 /**//*看一下System.Web.HttpApplication的类型声明
66 *public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable
67 *你会发现它同时实现了同步和异步的IHandler,所以在默认情况下,Asp.net对请求的处理是异步的。
68 */
69 if (applicationInstance is IHttpAsyncHandler)
70 {
71 IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
72 alt="" src="/imagelist/2007/236/b2dc295wopin.gif" align=top> extraData.AsyncAppHandler = handler2;
73 /**//*BeginProcessRequest会调用HttpApplication的ResumeSteps()方法,在ResumeSteps()中完成了整个应用程序周期的所有操作,
74 *包括对所有事件的触发、执行,对Handler的调用等。后文会有专门的小节进行介绍。*/
75 handler2.BeginProcessRequest(extraData, this._handlerCompletionCallback, extraData);
76 }
77 else
78 {
79 applicationInstance.ProcessRequest(extraData);
80 this.FinishRequest(extraData.WorkerRequest, extraData, null);
81 }
82 }
83 catch (Exception exception)
84 {
85 extraData.Response.InitResponseWriter();
86 this.FinishRequest(wr, extraData, exception);
87 }
88}

  从上面的代码可以看出,HttpApplication对象是经由HttpApplicationFactory.GetApplicationInstance(并最终调用HttpRuntime.CreateNonPublicInstance)创建的。HttpApplicationFactory对象会先解译目录中的Global.asax文件,接着加载虚拟目录内的Application Assembly(Global.dll),而后合并两者创建出一个Ghost Application Class,最后编译此Class后取得对象实例后返回至HttpR瀀?潳楬?untime对象,这个对象实例就是HttpApplication对象。解译与编译.asax文件的动作只发生于此虚拟目录第一次处理用户要求,或是Global.asax、Global.dll在前次执行后又做了变动时。至于HttpRuntime.CreateNonPublicInstance方法内部到底是通过什么样的代码,先后调用了Parser和Compiler,根据machine.config和web.config来生成一个HttpApplication对象,笔者未能通过自己的努力追踪到,盼高手补充。

9 7 3 1 2 3 4 8 :

  把此文章收藏到:          
广而告之
文章搜索
  • Google JZxue.Com

关于我们 | 联系我们 | 友情链接 | 网站地图
Copyright © 2005 - 2006 建站学 All rights reserved.