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

Because the CLR is a managed environment there are several components within the runtime that need to be initialised before any of your code can be executed. This post will take a look at the EE (Execution Engine) start-up routine and examine the initialisation process in detail.

(*) 68 is only a rough guide, it depends on which version of the runtime you are using, which features are enabled and a few other things


‘Hello World’

Imagine you have the simplest possible C# program, what has to happen before the CLR prints ‘Hello World’ out to the console?

using System;

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

The code path into the EE (Execution Engine)

When a .NET executable runs, control gets into the EE via the following code path:

  1. _CorExeMain() (the external entry point)

  2. _CorExeMainInternal()
  3. EnsureEEStarted()
  4. EEStartup()
  5. EEStartupHelper()

(if you’re interested in what happens before this, i.e. how a CLR Host can start-up the runtime, see my previous post ‘How the dotnet CLI tooling runs your code’)

And so we end up in EEStartupHelper(), which at a high-level does the following (from a comment in ceemain.cpp):

EEStartup is responsible for all the one time initialization of the runtime.
Some of the highlights of what it does include

  • Creates the default and shared, appdomains.
  • Loads mscorlib.dll and loads up the fundamental types (System.Object …)

The main phases in EE (Execution Engine) start-up routine

But let’s look at what it does in detail, the lists below contain all the individual function calls made from EEStartupHelper() (~500 L.O.C). To make them easier to understand, we’ll split them up into separate phases:

  • Phase 1 - Set-up the infrastructure that needs to be in place before anything else can run
  • Phase 2 - Initialise the core, low-level components
  • Phase 3 - Start-up the low-level components, i.e. error handling, profiling API, debugging
  • Phase 4 - Start the main components, i.e. Garbage Collector (GC), AppDomains, Security
  • Phase 5 - Final setup and then notify other components that the EE has started

Note some items in the list below are only included if a particular feature is defined at build-time, these are indicated by the inclusion on an ifdef statement. Also note that the links take you to the code for the function being called, not the line of code within EEStartupHelper().

Phase 1 - Set-up the infrastructure that needs to be in place before anything else can run

  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)

Phase 2 - Initialise the core, low-level components

  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)

Phase 3 - Start-up the low-level components, i.e. error handling, profiling API, debugging

  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()

Phase 4 - Start the main components, i.e. Garbage Collector (GC), AppDomains, Security

  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()

Phase 5 Final setup and then notify other components that the EE has started

  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===================

Once this is all done, the CLR is now ready to execute your code!!


Executing your code

Your code will be executed (after first being ‘JITted’) via the following code flow:

    1. CorHost2::ExecuteAssembly()

    2. Assembly::ExecuteMainMethod()
    3. RunMain() (in assembly.cpp)

在执行一行代码之前CLR做的68件事[The 68 things the CLR does before executing a single line of your code]的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. IntelliJ IDEA安装后需要必须做的一件事

    把Alt+斜杆 删除 Ctrl+空格修改成 Alt+斜杆 Ctrl+空格用过输入法的人都应该知道为什么要做上面一件事

随机推荐

  1. 基于canvas的仪表盘效果

    概述 基于Canvas实现的仪表盘及效果.通过配置参数,可以任意修改仪表盘颜色,刻度,动画过渡时间等,满足不同场景下的使用.同时使用原生的Canvas,也是学习Canvas的很好的例子. 详细 代码下 ...

  2. Ubuntu开机自动禁用无线网络

    让ubuntu开机自动禁用无线网络. 1.自启动脚本 将下面这条禁用无线网络的命令添加到“启动应用程序“中,这样开机时无线网络就会被自动禁用. dbus-send --system --type=me ...

  3. 使用vs调试.net源代码

    使用.NET Framework库参考源进行调试 您可能会想知道使用.NET Framework参考源的调试方式.在下面的示例中,您将看到一个我调用公用Console.WriteLine方法的工具.从 ...

  4. python --标准库 路径与文件 (os.path包, glob包)

    os.path包 os.path包主要是处理路径字符串,提取出有用信息. #coding:utf-8 import os.path path = 'D:\\Python7\\test\\data.tx ...

  5. ajax异步请求,session失效处理

    后台拦截器代码: // 判断是否是AJAX请求 if (isAjaxRequest(request)) { log.info("AjaxRequest请求"); ActionCon ...

  6. Redis配置和常用命令

    redis.conf配置文件: 引用 #是否作为守护进程运行 daemonize yes #配置pid的存放路径及文件名,默认为当前路径下 pidfile redis.pid #Redis默认监听端口 ...

  7. AutoFac文档6(转载)

    目录 开始 Registering components 控制作用域和生命周期 用模块结构化Autofac xml配置 与.net集成 深入理解Autofac 指导 关于 词汇表 实例生命周期 实例生 ...

  8. unity, standard shader消耗两个draw call

    假设场景中只放一个球,关掉阴影和skybox,球体使用Unlit/Texture shader,则draw call数为2(背景占一个draw call,球占一个draw call). 相同情况下若将 ...

  9. CentOS 安装 升级Firefox

    把旧版本的firefox卸掉: # yum erase firefox 然后安装新版本: # yum firefox

  10. mybatis的#和$的差别

    在项目中假设使用mybatis保存形如"1,2,3"这种字符串.你须要使用"#"号取值.假设使用$符号会报错, 假设是查询形如"id in (1,2, ...