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

样例代码

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

  1. using System;
  2.  
  3. namespace ConsoleApplication
  4. {
  5. public class Program
  6. {
  7. public static void Main(string[] args)
  8. {
  9. Console.WriteLine("Hello World!");
  10. }
  11. }
  12. }

进入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. Shell脚本——求随机数的最值

    写一个脚本,利用RANDOM生成10个随机数,并找出其中的最大值,和最小值: #!/bin/bash # MAX= MIN= ..};do RAN=$RANDOM [ $i -eq ] &&a ...

  2. 【插件】【idea】JRebel mybatisPlus extension是JRebel热部署插件的扩展支持mybatis的xml文件热部署

    和JRebel一起使用,修改mybatis的mapper.xml文件不用重启项目 File->Settings->Plugs

  3. SQL --------------- order by 排序

    首先创建一个表弄点数据 order by 关键字用于排序查询 默认按照升序(asc)进行排列 降序要使用 desc排序方式:数字按照大小 英文字母和汉字按照第一个字母 从 a-z 排列语     法: ...

  4. mysql备份、还原数据库(命令行)

    这里记录下MySQL如何通过命令行备份和还原数据库. 简单的三个步骤 方法很简单,可以分为三个步骤: 1.打开cmd控制台(命令行). 2.输入相应命令完成备份还原操作. 3.关闭cmd控制台. 就和 ...

  5. Appium+python自动化(一)- 环境搭建—上(超详解)

    简介 今天是高考各地由于降水,特别糟糕,各位考生高考加油,全国人民端午节快乐.最近整理了一下自动化的东西,先前整理的python接口自动化已经接近尾声.即将要开启新的征程和篇章(Appium& ...

  6. 04、状态模式(State)

    一.概念: 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类.[DP] 二.作用: 状态模式的主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况.吧状态的判断逻辑转 ...

  7. Winfrom devexpress 通用权限框架

    毕业到现在也快两年了,手上的项目也有好几个,但总感觉不是狠理想,近来把手上杂七杂八的项目整理了一下,结合各个项目的优点,重新开发了一套winfrom+devexpress 通用权限(CS)框架(BS版 ...

  8. 前端跨域之Jsonp实现原理及.Net下Jsonp的实现

    jsonp的本质是通过script标签的src属性请求到服务端,拿到到服务端返回的数据 ,因为src是可以跨域的.前端通过src发送跨域请求时在请求的url带上回调函数,服务端收到请求时,接受前端传过 ...

  9. RESTful规范总结

    思维导图xmind文件:https://files-cdn.cnblogs.com/files/benjieming/RESTful%E8%A7%84%E8%8C%83.zip

  10. Spring Boot 实战 入门

    目前没有系统学习过 Spring 框架,参与工作时,直接参与到了 Spring Boot 项目的开发.目前还比较菜,所以,你要是和我一样,不妨也跳过 Spring 框架的学习,直接学习 Sring B ...