SSCLI中GC源碼分析(1) - EE與BCL之間的調用接口FCall
首先在SSCLI2.0源代碼的\clr\src\vm\comutilnative.cpp文件中的GCInterface類的CollectGeneration方法上下個斷點,該宏方法主要實現了一個調用轉換:
/*==============================CollectGeneration=============================== **Action: Collects all generations <= args->generation **Returns: void **Arguments: args->generation: The maximum generation to collect **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration(); ==============================================================================*/ FCIMPL1(void, GCInterface::CollectGeneration, INT32 generation) { CONTRACTL { MODE_COOPERATIVE; DISABLED(GC_TRIGGERS); // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F. THROWS; SO_TOLERANT; } CONTRACTL_END; //We've already checked this in GC.cs, so we'll just assert it here. _ASSERTE(generation >= -1); //We don't need to check the top end because the GC will take care of that. HELPER_METHOD_FRAME_BEGIN_0(); GCHeap::GetGCHeap()->GarbageCollect(generation); if (g_TrapReturningThreads) { GetThread()->PulseGCMode(); } HELPER_METHOD_FRAME_END(); } FCIMPLEND
這個方法提供了從BCL中調用SSCLI虛擬執行引擎內部功能的一個接口。在SSCLI實現版本中,這種調用轉換的方式叫做FCall。
在不會被JIT的代碼中,譬如一些Helper代碼片段或者stubs中,經常會使用一些調用轉換來協調不同功能的組件。這樣在Runtime中,就不會出現是一個非常大的、包含了所有功能的Heap。
使用這種非常有效的調用轉換,可以讓托管高級語言,譬如C#,在用戶代碼中調用Runtime中的內部功能。使用這種調用,只需要給實現的方法加上MethodImplOptions.InternalCall的屬性即可。
而FCall會使用sscli20\clr\src\vm\ecall.cpp中的ECFunc結構體,來完成從托管方法到Runtime內部實現的C++方法的轉換:
struct ECFunc { UINT_PTR m_dwFlags; #ifndef DACCESS_COMPILE LPVOID m_pImplementation; #else TADDR m_pImplementation; #endif PTR_MethodDesc m_pMD; // for reverse mapping PTR_ECFunc m_pNext; // linked list for hash table LPCUTF8 m_wszMethodName; LPHARDCODEDMETASIG m_wszMethodSig; // Optional field. It is valid only if HasSignature() is set. bool IsEndOfArray() { LEAF_CONTRACT; return !!(m_dwFlags & FCFuncFlag_EndOfArray); } bool HasSignature() { LEAF_CONTRACT; return !!(m_dwFlags & FCFuncFlag_HasSignature); } bool IsUnreferenced(){ LEAF_CONTRACT; return !!(m_dwFlags & FCFuncFlag_Unreferenced); } CorInfoIntrinsics IntrinsicID() { LEAF_CONTRACT; return (CorInfoIntrinsics)((INT8)(m_dwFlags >> 16)); } int DynamicID() { LEAF_CONTRACT; return (int) ((INT8)(m_dwFlags >> 24)); } ECFunc* NextInArray() { LEAF_CONTRACT; return (ECFunc*)((BYTE*)this + (HasSignature() ? sizeof(ECFunc) : offsetof(ECFunc, m_wszMethodSig))); } };
m_wszMethodName表示的是BCL中對應的方法。m_pImplementation表示的是Runtime中對應的方法。可以看到,在這種轉換中,不涉及到任何關于參數傳遞或者是類型檢查之類的邏輯,因為FCall調用的方法,完全是Runtime內部實現的。
在Method.cpp文件中的MethodClassification枚舉類型中,還列出了其他SSCLI執行引擎中對Method分類的:
// Used in MethodDesc enum MethodClassification { mcIL = 0, // IL mcFCall = 1, // FCall (also includes tlbimped ctor, Delegate ctor) mcNDirect = 2, // N/Direct mcEEImpl = 3, // special method; implementation provided by EE (like Delegate Invoke) mcArray = 4, // Array ECall mcInstantiated = 5, // Instantiated generic methods, including descriptors // for both shared and unshared code (see InstantiatedMethodDesc) mcDynamic = 7, // for method dewsc with no metadata behind mcCount, };
在SSCLI的對象內存布局中,由于MethodDesc結構是幾個不同的類型的聚合體,所以這里使用一個三個bit位的flag來表示MethodDesc是使用的那種類型。在MethodClassfication中,并不表示方法是JITed或者是NON-JITed。因為是否被JIT過,只有在方法第一次被執行的時候才能夠知道。同時,由于托管進程中的線程都需要修改這三個BIT位,所以這個標識被放在可以被線程同步范圍的內存地址上。
MethodClassification這個結構會在MethodDesc中被使用到,來標識一個Method類型。同時,MethodDesc中有一個16bit的flag(MethodClassification)來表示一個MethodDesc所有的屬性。可以參考前面章節中關于MethodDesc的介紹。
這里,還有一點需要注意的是,在調用GCHeap類中的GarbageCollect方法之前,下面的代碼會在Stack中安裝一個棧幀:
//We don't need to check the top end because the GC will take care of that. HELPER_METHOD_FRAME_BEGIN_0(); GCHeap::GetGCHeap()->GarbageCollect(generation); if (g_TrapReturningThreads) { GetThread()->PulseGCMode(); } HELPER_METHOD_FRAME_END();
在上面的方法中,HELPER_METHOD_FRAME_BEGIN_0()方法HELPER_METHOD_FRAME_END()成對使用,用來在GCHeap中放置HelperMethodFrame棧幀。這個棧幀的主要功能,是允許加入Jit Helper或者是標識FCall的相關信息到棧中,來方便程序對Stack的遍歷。下面是這個Frame的構造函數:
// Lazy initialization of HelperMethodFrame. Need to // call InsureInit to complete initialization // If this is an FCall, the second param is the entry point for the FCALL. // The MethodDesc will be looked up form this (lazily), and this method // will be used in stack reporting, if this is not an FCall pass a 0 HelperMethodFrame(void* fCallFtnEntry, struct LazyMachState* ms, unsigned attribs = 0) { WRAPPER_CONTRACT; INDEBUG(memset(&m_Attribs, 0xCC, sizeof(HelperMethodFrame) - offsetof(HelperMethodFrame, m_Attribs));) m_Attribs = attribs; LazyInit(fCallFtnEntry, ms); }
順便提一下,這個地方使用了Lazy initialization技術,Lazy initialization是一種延遲初始化對象的策略,譬如說計算一個值,或者是一個Process的計算代價比較昂貴而且也不是經常使用的情況下,就在第一次使用的時候初始化。實現的方式,主要是用一個Flag來標識這個過程是否已經開始。這也算是一種設計模式。可以在Wikipedia找到關于這個技術的比較詳細的說明:
http://en.wikipedia.org/wiki/Lazy_initialization
最后,在comutilnative.cpp文件中,還是實現了許多其他的BCL和Runtime之間調用的類和方法,主要包括一下類:
posted on 2009-09-11 11:13 lbq1221119 閱讀(2600) 評論(20) 收藏 舉報
浙公網安備 33010602011771號