因为CLR是一个托管环境,所以运行时中有几个组件需要在执行任何代码之前初始化。本文将介绍EE(执行引擎)启动程序,并详细检查初始化过程。68只是一个粗略的指南,它取决于您使用的运行时版本、启用了哪些功能以及其他一些东西。

样例代码

假设你有一个最简单的C#程序,在CLR将“Hello World”输出到控制台之前会发生什么?

using System;

namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}

进入EE(执行引擎)的代码路径

当.NET可执行文件运行时,通过以下代码路径进入EE:

  1. _CorExeMain() (the external entry point)

    • call to _CorExeMainInternal()
  2. _CorExeMainInternal()
    • call to EnsureEEStarted()
  3. EnsureEEStarted()
    • call to EEStartup()
  4. EEStartup()
    • call to EEStartupHelper()
  5. EEStartupHelper()

因此,我们在EEStartupHelper()中结束,它在高层执行以下操作(来自ceemain.cpp中的注释)---EEStartup负责运行时的所有一次性初始化:

  • 创建默认和共享的appdomains。
  • 加载mscorlib.dll并加载基本类型(System.Object…)

EE(执行引擎)启动程序的主要阶段

下面的列表包含了从EEStartupHelper()调用的所有单独函数(~500 L.O.C)。为了使它们更容易理解,我们将它们分为不同的阶段:

  • 第1阶段-在运行其他任何东西之前,设置需要到位的基础设施
  • 第2阶段-初始化核心低层组件
  • 第3阶段-启动低级组件,即错误处理、Profiling  API、调试
  • 阶段4-启动主要组件,即垃圾收集器(GC)、AppDomains、安全性
  • 第5阶段-最终设置,然后通知其他组件EE已启动
注意:下面列表中的某些项仅在生成时定义了特定功能时才包含,这些项由ifdef语句中的包含来指示。还要注意的是,这些链接指向要调用的函数的代码,而不是EEStartupHelper()中的代码行。

第1阶段-在运行其他任何东西之前,设置需要到位的基础设施

  1. Wire-up console handling - SetConsoleCtrlHandler(..) (ifndef FEATURE_PAL)
  2. Initialise the internal SString class (everything uses strings!) - SString::Startup()
  3. Make sure the configuration is set-up, so settings that control run-time options can be accessed - EEConfig::Set-up() and InitializeHostConfigFile() (#if !defined(CROSSGEN_COMPILE))
  4. Initialize Numa and CPU group information - NumaNodeInfo::InitNumaNodeInfo() and CPUGroupInfo::EnsureInitialized() (#ifndef CROSSGEN_COMPILE)
  5. Initialize global configuration settings based on startup flags - InitializeStartupFlags()
  6. Set-up the Thread Manager that gives the runtime access to the OS threading functionality (StartThread(), Join(), SetThreadPriority() etc) - InitThreadManager()
  7. Initialize Event Tracing (ETW) and fire off the CLR startup events - InitializeEventTracing() and ETWFireEvent(EEStartupStart_V1) (#ifdef FEATURE_EVENT_TRACE)
  8. Set-up the GS Cookie (Buffer Security Check) to help prevent buffer overruns - InitGSCookie()
  9. Create the data-structures needed to hold the ‘frames’ used for stack-traces - Frame::Init()
  10. Ensure initialization of Apphacks environment variables - GetGlobalCompatibilityFlags() (#ifndef FEATURE_CORECLR)
  11. Create the diagnostic and performance logs used by the runtime - InitializeLogging() (#ifdef LOGGING) and PerfLog::PerfLogInitialize() (#ifdef ENABLE_PERF_LOG)

第2阶段-初始化核心低层组件

  1. Write to the log ===================EEStartup Starting===================
  2. Ensure that the Runtime Library functions (that interact with ntdll.dll) are enabled - EnsureRtlFunctions() (#ifndef FEATURE_PAL)
  3. Set-up the global store for events (mutexes, semaphores) used for synchronisation within the runtime - InitEventStore()
  4. Create the Assembly Binding logging mechanism a.k.a Fusion - InitializeFusion() (#ifdef FEATURE_FUSION)
  5. Then initialize the actual Assembly Binder infrastructure - CCoreCLRBinderHelper::Init() which in turn calls AssemblyBinder::Startup() (#ifdef FEATURE_FUSION is NOT defined)
  6. Set-up the heuristics used to control Monitors, Crsts, and SimpleRWLocks - InitializeSpinConstants()
  7. Initialize the InterProcess Communication with COM (IPC) - InitializeIPCManager() (#ifdef FEATURE_IPCMAN)
  8. Set-up and enable Performance Counters - PerfCounters::Init() (#ifdef ENABLE_PERF_COUNTERS)
  9. Set-up the CLR interpreter - Interpreter::Initialize() (#ifdef FEATURE_INTERPRETER), turns out that the CLR has a mode where your code is interpreted instead of compiled!
  10. Initialise the stubs that are used by the CLR for calling methods and triggering the JIT - StubManager::InitializeStubManagers(), also Stub::Init() and StubLinkerCPU::Init()
  11. Set up the core handle map, used to load assemblies into memory - PEImage::Startup()
  12. Startup the access checks options, used for granting/denying security demands on method calls - AccessCheckOptions::Startup()
  13. Startup the mscorlib binder (used for loading “known” types from mscorlib.dll) - MscorlibBinder::Startup()
  14. Initialize remoting, which allows out-of-process communication - CRemotingServices::Initialize() (#ifdef FEATURE_REMOTING)
  15. Set-up the data structures used by the GC for weak, strong and no-pin references - Ref_Initialize()
  16. Set-up the contexts used to proxy method calls across App Domains - Context::Initialize()
  17. Wire-up events that allow the EE to synchronise shut-down - g_pEEShutDownEvent->CreateManualEvent(FALSE)
  18. Initialise the process-wide data structures used for reader-writer lock implementation - CRWLock::ProcessInit() (#ifdef FEATURE_RWLOCK)
  19. Initialize the debugger manager - CCLRDebugManager::ProcessInit() (#ifdef FEATURE_INCLUDE_ALL_INTERFACES)
  20. Initialize the CLR Security Attribute Manager - CCLRSecurityAttributeManager::ProcessInit() (#ifdef FEATURE_IPCMAN)
  21. Set-up the manager for Virtual call stubs - VirtualCallStubManager::InitStatic()
  22. Initialise the lock that that GC uses when controlling memory pressure - GCInterface::m_MemoryPressureLock.Init(CrstGCMemoryPressure)
  23. Initialize Assembly Usage Logger - InitAssemblyUsageLogManager() (#ifndef FEATURE_CORECLR)

第3阶段-启动低级组件,即错误处理、Profiling API、调试

  1. Set-up the App Domains used by the CLR - SystemDomain::Attach() (also creates the DefaultDomain and the SharedDomain by calling SystemDomain::CreateDefaultDomain() and SharedDomain::Attach())
  2. Start up the ECall interface, a private native calling interface used within the CLR - ECall::Init()
  3. Set-up the caches for the stubs used by delegates - COMDelegate::Init()
  4. Set-up all the global/static variables used by the EE itself - ExecutionManager::Init()
  5. Initialise Watson, for windows error reporting - InitializeWatson(fFlags) (#ifndef FEATURE_PAL)
  6. Initialize the debugging services, this must be done before any EE thread objects are created, and before any classes or modules are loaded - InitializeDebugger() (#ifdef DEBUGGING_SUPPORTED)
  7. Activate the Managed Debugging Assistants that the CLR provides - ManagedDebuggingAssistants::EEStartupActivation() (ifdef MDA_SUPPORTED)
  8. Initialise the Profiling API - ProfilingAPIUtility::InitializeProfiling() (#ifdef PROFILING_SUPPORTED)
  9. Initialise the exception handling mechanism - InitializeExceptionHandling()
  10. Install the CLR global exception filter - InstallUnhandledExceptionFilter()
  11. Ensure that the initial runtime thread is created - SetupThread() in turn calls SetupThread(..)
  12. Initialise the PreStub manager (PreStub’s trigger the JIT) - InitPreStubManager() and the corresponding helpers StubHelpers::Init()
  13. Initialise the COM Interop layer - InitializeComInterop() (#ifdef FEATURE_COMINTEROP)
  14. Initialise NDirect method calls (lazy binding of unmanaged P/Invoke targets) - NDirect::Init()
  15. Set-up the JIT Helper functions, so they are in place before the execution manager runs - InitJITHelpers1() and InitJITHelpers2()
  16. Initialise and set-up the SyncBlock cache - SyncBlockCache::Attach() and SyncBlockCache::Start()
  17. Create the cache used when walking/unwinding the stack - StackwalkCache::Init()

阶段4-启动主要组件,即垃圾收集器(GC)、AppDomains、安全性

  1. Start up security system, that handles Code Access Security (CAS) - Security::Start() which in turn calls SecurityPolicy::Start()
  2. Wire-up an event to allow synchronisation of AppDomain unloads - AppDomain::CreateADUnloadStartEvent()
  3. Initialise the ‘Stack Probes’ used to setup stack guards InitStackProbes() (#ifdef FEATURE_STACK_PROBE)
  4. Initialise the GC and create the heaps that it uses - InitializeGarbageCollector()
  5. Initialise the tables used to hold the locations of pinned objects - InitializePinHandleTable()
  6. Inform the debugger about the DefaultDomain, so it can interact with it - SystemDomain::System()->PublishAppDomainAndInformDebugger(..) (#ifdef DEBUGGING_SUPPORTED)
  7. Initialise the existing OOB Assembly List (no idea?) - ExistingOobAssemblyList::Init() (#ifndef FEATURE_CORECLR)
  8. Actually initialise the System Domain (which contains mscorlib), so that it can start executing - SystemDomain::System()->Init()

第5阶段最终设置,然后通知其他组件EE已经启动了第4阶段-启动主要组件,即垃圾收集器(GC)、AppDomains、Security

  1. Tell the profiler we’ve stated up - SystemDomain::NotifyProfilerStartup() (#ifdef PROFILING_SUPPORTED)
  2. Pre-create a thread to handle AppDomain unloads - AppDomain::CreateADUnloadWorker() (#ifndef CROSSGEN_COMPILE)
  3. Set a flag to confirm that ‘initialisation’ of the EE succeeded - g_fEEInit = false
  4. Load the System Assemblies (‘mscorlib’) into the Default Domain - SystemDomain::System()->DefaultDomain()->LoadSystemAssemblies()
  5. Set-up all the shared static variables (and String.Empty) in the Default Domain - SystemDomain::System()->DefaultDomain()->SetupSharedStatics(), they are all contained in the internal class SharedStatics.cs
  6. Set-up the stack sampler feature, that identifies ‘hot’ methods in your code - StackSampler::Init() (#ifdef FEATURE_STACK_SAMPLING)
  7. Perform any once-only SafeHandle initialization - SafeHandle::Init() (#ifndef CROSSGEN_COMPILE)
  8. Set flags to indicate that the CLR has successfully started - g_fEEStarted = TRUE, g_EEStartupStatus = S_OK and hr = S_OK
  9. Write to the log ===================EEStartup Completed===================
 一旦这些都完成了,CLR现在就可以执行您的代码了!

执行用户代码

您的代码将通过以下代码流执行(在第一次被“JITted”之后):

  1. CorHost2::ExecuteAssembly()

    • calling ExecuteMainMethod()
  2. Assembly::ExecuteMainMethod()
    • calling RunMain()
  3. RunMain() (in assembly.cpp)
    • eventually calling into you main() method
    • full explanation of the ‘call’ process

转自:https://mattwarren.org/2017/02/07/The-68-things-the-CLR-does-before-executing-a-single-line-of-your-code/

在执行一行代码之前CLR做的68件事的更多相关文章

  1. 在执行一行代码之前CLR做的68件事[The 68 things the CLR does before executing a single line of your code]

    待翻译,原文地址:http://mattwarren.org/2017/02/07/The-68-things-the-CLR-does-before-executing-a-single-line- ...

  2. 安装 Kali Linux 后需要做的 20 件事

    安装 Kali Linux 后需要做的 20 件事 本文含有我觉得有用的每一件事情.本文分为三大部分: 专门针对Kali用户 Kali Linux是来自Debian的一个特殊版本,Kali Linux ...

  3. 安装 CentOS 7 后必做的七件事

    原文 安装 CentOS 7 后必做的七件事 CentOS 是最多人用来运行服务器的 Linux 版本,最新版本是 CentOS 7.当你兴趣勃勃地在一台主机或 VPS 上安装 CentOS 7 后, ...

  4. 刚安装Fedora 23工作站后,你必须要做的24件事

    [51CTO.com快译]Fedora 23工作站版本已发布,此后我们就一直在密切关注它.我们已经为新来读者介绍了一篇安装指南:<Fedora 23工作站版本安装指南> 还有一篇介绍如何从 ...

  5. 新手学习SEO要做的七件事是什么?

    学习SEO可能不那么先进的编程,学习SEO不可能掌握网页设计,学习SEO不需要学习SEO DIV + CSS;不是一个困难的任务,但是在学习过程中,如果你想掌握SEO,那么我们要做的几件事. 1.学习 ...

  6. 在 PHP 7 中不要做的 10 件事

    在 PHP 7 中不要做的 10 件事 1. 不要使用 mysql_ 函数 这一天终于来了,从此你不仅仅“不应该”使用mysql_函数.PHP 7 已经把它们从核心中全部移除了,也就是说你需要迁移到好 ...

  7. Ubuntu装完后要做的几件事

    Ubuntu装完后要做的几件事 改hosts 无论哪里,改hosts都是第一件事,没hosts咋google.没google咋活.在终端输入命令 sudo gedit /etc/hosts在# The ...

  8. 安装完Ubuntu 14.04要做的九件事

    www.linuxidc.com/Linux/2014-04/100411.htm 1.看看有哪些新特性 安装完之后的第一件事肯定是看看Ubuntu 14.04有哪些新的特性. Ubuntu 14.0 ...

  9. 亲身体验:digitalocean vps能做的10件事

    我写过一篇亲身体验:digitalocean和linode评测哪个好,帮助不少网友选购价格便宜性能优异的免备案vps,相信大家对两家产品有所了解.vps的性能和用途远远超过传统的虚拟主机,你拥有独立I ...

随机推荐

  1. k8s之系统组件架构-02

    k8s系统架构图 网络组件:calico+kube-proxy(IPVS) 网络暴露:traefik+ingress,分别对HTTP与TCP的服务暴露 存储:glusterfs(heketi管理) 日 ...

  2. Python 中把一个list 列表分组/分块

    比如:将list:[1,2,3,4,5,6,7,8,9]按照下标顺序分成3组:[1,2,3] [4,5,6] [7,8,9]或分成5组:[1,2,] [3, 4] [5,6] [7, 8] [ 9 ] ...

  3. sqlite删除数据

    sqlite删除数据 某日, 在使用sqlite的时候发现查询速度极慢, 查看了一下文件大小, sqlite数据库文件已经达到了 22G! 对于一个文件数据库来说, 已经相当致命了,先不说后面如果解决 ...

  4. Unity Shader 2D水流效果

    水流的模拟主要运用了顶点变换和纹理动画的结合: 顶点变换中,利用正弦函数模拟河流的大致形态,例如波长,振幅等. 纹理动画中,将纹理坐标朝某一方向持续滚动以形成流动的效果. 脚本如下: Shader & ...

  5. FutureTask源码

    FutureTask可用于异步获取执行结果或取消执行任务的场景.通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过Fu ...

  6. Zookeeper在linux上的安装

    1:进入 cd  /usr/local目录下 2:创建zookeeper目录  midir zookeeper 3:将压缩包复制到zookeeper目录下  cp /root/zookeeper/zo ...

  7. 奥展项目笔记02--一个bat文件运行多个java jar包

    奥展项目中后端微服务有很多jar包,一个一个启动又费时间效率又低,怎么才能一下让所有的jar包一块运行呢?我们可以编写.bat文件来一键启动. 1.我们将.bat文件放到jar包的同一级目录文件夹中: ...

  8. fiverr无法注册的解决办法,fiverr注册教程

    转载 https://www.wok99.com/450.html

  9. 使用 Spring Boot 构建 RESTful API

    1. 使用 Idea 创建 Spring Initializer 项目 在创建项目的对话框中添加 Web 和 Lombok,或者建立项目后在 pom.xml 中添加依赖: <dependency ...

  10. C语言语法教程-链表

    链表是一群结构体(称为结点)通过指针连起来.这种结构体类型,比较特殊,叫自引用结构体类型.它有一个指针指向和和结构体一样的类型,其余是数据成员. 头指针指向第一结点,尾指针一定要用空表示,这叫有头有尾 ...