惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

TaoSecurity Blog
TaoSecurity Blog
博客园 - 司徒正美
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Stack Overflow Blog
Stack Overflow Blog
The GitHub Blog
The GitHub Blog
Google DeepMind News
Google DeepMind News
Security Archives - TechRepublic
Security Archives - TechRepublic
宝玉的分享
宝玉的分享
N
News and Events Feed by Topic
The Hacker News
The Hacker News
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
F
Full Disclosure
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
H
Hacker News: Front Page
L
LangChain Blog
Microsoft Security Blog
Microsoft Security Blog
Y
Y Combinator Blog
B
Blog RSS Feed
H
Heimdal Security Blog
Google Online Security Blog
Google Online Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 三生石上(FineUI控件)
V2EX - 技术
V2EX - 技术
V
Vulnerabilities – Threatpost
Help Net Security
Help Net Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Tailwind CSS Blog
W
WeLiveSecurity
T
Tenable Blog
D
DataBreaches.Net
Martin Fowler
Martin Fowler
Cyberwarzone
Cyberwarzone
Cisco Talos Blog
Cisco Talos Blog
S
Secure Thoughts
O
OpenAI News
L
LINUX DO - 热门话题
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Jina AI
Jina AI
J
Java Code Geeks
Know Your Adversary
Know Your Adversary
IT之家
IT之家
Latest news
Latest news
Cloudbric
Cloudbric

博客园 - lbq1221119

时间同步算法与Simple Ring-based election algorithm算法分析 SSCLI中GC垃圾回收源码分析(3) - GarbageCollectGeneration()与SuspendEE SSCLI中GC垃圾回收源码分析(2) - GarbageCollect()与Spin Lock (北京宇思信德科技公司)诚聘C#软件工程师 , Middle及Senior Level .Net Micro framework在开发过程中的bugs/problems及解决方案。 MF中使用GPRS:如何通过一个串口终端实现GPRS Modem拨号上网 博客园北京俱乐部第三次活动讲义PPT ValueType.Equals(null)的底层实现及CLR虚拟机对其结构支持 WebService传输数据流及数据交互解析 托管线程退出之后Dump文件特征 国外精品研究论文(持续更新) Random()中具体实现(含种子数组的实现) From double Click to Main: PAL initialization - lbq1221119 From double Click to Main: The initialization of Process in OS Microsoft Micro Framework 3.0对Serial Peripheral Interface 的支持 使用CorDbg进行托管调试 CLR内核调试之:Malloc函数实现 Build SSCLI20 under VS2008 full Document (完全手册) MethodTable内存空间分配中加法运算算法解析
SSCLI中GC源码分析(1) - EE与BCL之间的调用接口FCall
lbq1221119 · 2009-09-11 · via 博客园 - lbq1221119

首先在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之间调用的类和方法,主要包括一下类:

image