從.NET類庫代碼來看ASP.NET運行時

發表于:2008-04-08來源:作者:點擊數: 標簽:
寫在前面的話:網上講Asp.net運行模式的好文章已經很多了,筆者本不用多此一舉,另成一文。但從筆者自己的學習經驗看,如果學到的這些知識不能對應到類庫中的源代碼,印象總歸不夠深刻,大有隔靴搔癢之感。只好自己寫上一篇,對這方面的知識做個小小的總結。
寫在前面的話:網上講Asp.net運行模式的好文章已經很多了,筆者本不用多此一舉,另成一文。但從筆者自己的學習經驗看,如果學到的這些知識不能對應到類庫中的源代碼,印象總歸不夠深刻,大有隔靴搔癢之感。只好自己寫上一篇,對這方面的知識做個小小的總結。文中所有內容都是筆者在看了網上很多文章后,結合自己的開發經驗得出的一些理解,難免有錯誤的地方,歡迎批評指出。另外,由于筆者能力所限,很多地方并未說透(真正對應到代碼),也盼高手能夠給予補充。

  一.進入Asp.net運行時之前

  雖然本文的重點是對托管代碼的解析,但為了整個知識點的完整性,這里簡單介紹一下IIS處理請求的一些基本情況。在一個IIS服務器上,你可以設置多個應用程序池(每個應用程序池可以單獨設置允許使用的最大內存數量、CPU使用率、回收工作進程的時間間隔等參數,而且一個應用程序池里面只能使用一個版本的.NET Framework),然后把自己的Web應用分別部署到這些應用程序池中。在默認情況下,每個應用池會有一個工作進程w3wp.exe來維護(如果開通了Web園功能,也可以設置多個工作進程)。每個應用程序(虛擬目錄)在池中都有自己的應用程序域,這些應用程序域都處于這個應用程序池的工作進程的進程空間內。

  IIS是通過各種ISAPI的擴展來處理各種類型的應用的。當我們從客戶端提交一個請求過來之后,IIS會根據請求的頁面或者服務的類型,把請求映射到指定的ISAPI擴展。比方說,如果我們需要讓IIS支持perl這樣的服務器端程序(當然,這個移植工作早就有人做過了),我們就需要編寫一個專門處理對perl頁面進行的請求的ISAPI擴展。根據ISAPI的定義(符合這個定義的ISAPI擴展才能和IIS正常交互),在你的擴展中可以包括ISAPI Extension和ISAPI Filter兩大部分。ISAPI Extension是對請求的處理程序,完成和web服務器之間的輸入輸出;而ISAPI Filter則是一些回調接口,你可以通過實現這些接口來介入到整個請求處理的每一步驟,對Authentication,RevolveCache等環節進行控制。另外,ISAPI本身就是在工作進程里運行的,而asp.net運行時也是在工作進程里運行的,所以兩者的交互非常有效率。

  對于.aspx頁面,這個擴展就是aspnet_isapi.dll。因為這些ISAPI都是非托管的Win32應用,直接對它們進行改動是比較困難的。所以,為了增強Asp.net運行時的可擴展性,aspnet_isapi.dll本身的功能非常少,我們可以把aspnet_isapi.dll簡單理解為請求信息的路由器,負責把請求從IIS傳送到asp.net運行時。而后面我們將要講到的HttpHandle和HttpModule則分別擔負起了ISAPI Extension和ISAPI Filter的功能,幸運的是,HttpHandle和HttpModule可以由純的托管代碼來實現。

  二.從非托管代碼到托管代碼

  前面說了,aspnet_isapi.dll是非托管代碼,而asp.net運行時是托管代碼,他們都運行在w3wp.exe工作進程里面,那么兩者之間的調用點發生在什么地方呢?在介紹接下來的內容之前必須先介紹一個概念:ECB。ECB的全稱是Extension Control Block,它是一個非托管資源包,具有對ISAPI接口完整的訪問能力,包含了所有和一個傳入請求有關的底層信息,如提交的標單中的數據等等。所以說,asp.net中的托管代碼想要訪問aspnet_isapi.dll對外提供的接口,就需要通過ECB。其實更準確的來說,是托管代碼公布了一個IUnknown類型的接口供aspnet_isapi.dll調用,而aspnet_isapi.dll在調用的時候會把自己的ecb地址傳進去。

  明白了ECB的概念,下面我們要介紹一個接口和一個接口的實現類(位于System.Web.Hosting名字空間下),請讀者注意筆者在代碼中的注釋(本文的主要目的就是和大家一起從代碼實現的角度來認識整個Asp.net運行時,所以代碼里的注釋是筆者添加的關鍵性說明,后面的所有代碼段都是這樣):

1/**//*InterfaceType(ComInterfaceType.InterfaceIsIUnknown)指明了這個接口將作為 IUnknown 派生接口向 COM 公開,這就使得isapi.dll可以以COM方式調用此接口。*/
2[ComImport, Guid("08a2c56f-7c16-41c1-a8be-432917a1a2d1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
3public interface IISAPIRuntime
4{
5 void StartProcessing();
6 void StopProcessing();
7 /**//*ProcessRequest方法就是整個處理流程中托管代碼和非托管代碼的分界點,可以看到里面是以一個IntPtr結構傳入了調用方(也就是isapi.dll)的ECB地址*/
8 [return: MarshalAs(UnmanagedType.I4)]
9 int ProcessRequest([In] IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)] int useProcessModel);
10 void DoGCCollect();
11}
12
13/**//*這個類實現了IISAPIRuntime接口。它的實例對象存在于每一個AppDomain中,作為整個Asp.net運行時的入口。*/
14public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IRegisteredObject
15{
16 // Fields
17 private static int _isThisAppDomainRemovedFromUnmanagedTable;
18 private static string s_thisAppDomainsIsapiAppId;
19
20 // Methods
21 [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Minimal), SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
22 public ISAPIRuntime();
23 public void DoGCCollect();
24 public override object InitializeLifetimeService();
25 /**//*處理請求的入口點方法,由isapi.dll以COM方式調用*/
26 public int ProcessRequest(IntPtr ecb, int iWRType);
27 internal static void RemoveThisAppDomainFromUnmanagedTable();
28 internal void SetThisAppDomainsIsapiAppId(string appId);
29 public void StartProcessing();
30 public void StopProcessing();
31 void IRegisteredObject.Stop(bool immediate);
32}

  所以,一切都是從aspnet_isapi.dll以COM方式調用了一個ISAPIRuntime對象的ProcessRequest方法開始的??梢远嗵嵋痪涞氖?,這種調用是異步的,也就是說,aspnet_isapi.dll在調用后會立即返回,但ECB會一直保留下來,直到整個請求被處理完畢之后再釋放。

原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97