在执行一行代码之前CLR做的68件事[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:
- _CorExeMain() (the external entry point)
- call to _CorExeMainInternal()
- _CorExeMainInternal()
- call to EnsureEEStarted()
- EnsureEEStarted()
- call to EEStartup()
- EEStartup()
- call to EEStartupHelper()
- 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
- Wire-up console handling - SetConsoleCtrlHandler(..) (
ifndef FEATURE_PAL
) - Initialise the internal
SString
class (everything uses strings!) - SString::Startup() - 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)
) - Initialize Numa and CPU group information - NumaNodeInfo::InitNumaNodeInfo() and CPUGroupInfo::EnsureInitialized() (
#ifndef CROSSGEN_COMPILE
) - Initialize global configuration settings based on startup flags - InitializeStartupFlags()
- Set-up the Thread Manager that gives the runtime access to the OS threading functionality (
StartThread()
,Join()
,SetThreadPriority()
etc) - InitThreadManager() - Initialize Event Tracing (ETW) and fire off the CLR startup events - InitializeEventTracing() and ETWFireEvent(EEStartupStart_V1) (
#ifdef FEATURE_EVENT_TRACE
) - Set-up the GS Cookie (Buffer Security Check) to help prevent buffer overruns - InitGSCookie()
- Create the data-structures needed to hold the ‘frames’ used for stack-traces - Frame::Init()
- Ensure initialization of Apphacks environment variables - GetGlobalCompatibilityFlags() (
#ifndef FEATURE_CORECLR
) - 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
- Write to the log
===================EEStartup Starting===================
- Ensure that the Runtime Library functions (that interact with ntdll.dll) are enabled - EnsureRtlFunctions() (
#ifndef FEATURE_PAL
) - Set-up the global store for events (mutexes, semaphores) used for synchronisation within the runtime - InitEventStore()
- Create the Assembly Binding logging mechanism a.k.a Fusion - InitializeFusion() (
#ifdef FEATURE_FUSION
) - Then initialize the actual Assembly Binder infrastructure - CCoreCLRBinderHelper::Init() which in turn calls AssemblyBinder::Startup() (
#ifdef FEATURE_FUSION
is NOT defined) - Set-up the heuristics used to control Monitors, Crsts, and SimpleRWLocks - InitializeSpinConstants()
- Initialize the InterProcess Communication with COM (IPC) - InitializeIPCManager() (
#ifdef FEATURE_IPCMAN
) - Set-up and enable Performance Counters - PerfCounters::Init() (
#ifdef ENABLE_PERF_COUNTERS
) - 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! - Initialise the stubs that are used by the CLR for calling methods and triggering the JIT - StubManager::InitializeStubManagers(), also Stub::Init() and StubLinkerCPU::Init()
- Set up the core handle map, used to load assemblies into memory - PEImage::Startup()
- Startup the access checks options, used for granting/denying security demands on method calls - AccessCheckOptions::Startup()
- Startup the mscorlib binder (used for loading “known” types from mscorlib.dll) - MscorlibBinder::Startup()
- Initialize remoting, which allows out-of-process communication - CRemotingServices::Initialize() (
#ifdef FEATURE_REMOTING
) - Set-up the data structures used by the GC for weak, strong and no-pin references - Ref_Initialize()
- Set-up the contexts used to proxy method calls across App Domains - Context::Initialize()
- Wire-up events that allow the EE to synchronise shut-down -
g_pEEShutDownEvent->CreateManualEvent(FALSE)
- Initialise the process-wide data structures used for reader-writer lock implementation - CRWLock::ProcessInit() (
#ifdef FEATURE_RWLOCK
) - Initialize the debugger manager - CCLRDebugManager::ProcessInit() (
#ifdef FEATURE_INCLUDE_ALL_INTERFACES
) - Initialize the CLR Security Attribute Manager - CCLRSecurityAttributeManager::ProcessInit() (
#ifdef FEATURE_IPCMAN
) - Set-up the manager for Virtual call stubs - VirtualCallStubManager::InitStatic()
- Initialise the lock that that GC uses when controlling memory pressure - GCInterface::m_MemoryPressureLock.Init(CrstGCMemoryPressure)
- Initialize Assembly Usage Logger - InitAssemblyUsageLogManager() (
#ifndef FEATURE_CORECLR
)
Phase 3 - Start-up the low-level components, i.e. error handling, profiling API, debugging
- 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())
- Start up the ECall interface, a private native calling interface used within the CLR - ECall::Init()
- Set-up the caches for the stubs used by
delegates
- COMDelegate::Init() - Set-up all the global/static variables used by the EE itself - ExecutionManager::Init()
- Initialise Watson, for windows error reporting - InitializeWatson(fFlags) (
#ifndef FEATURE_PAL
) - 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
) - Activate the Managed Debugging Assistants that the CLR provides - ManagedDebuggingAssistants::EEStartupActivation() (
ifdef MDA_SUPPORTED
) - Initialise the Profiling API - ProfilingAPIUtility::InitializeProfiling() (
#ifdef PROFILING_SUPPORTED
) - Initialise the exception handling mechanism - InitializeExceptionHandling()
- Install the CLR global exception filter - InstallUnhandledExceptionFilter()
- Ensure that the initial runtime thread is created - SetupThread() in turn calls SetupThread(..)
- Initialise the PreStub manager (PreStub’s trigger the JIT) - InitPreStubManager() and the corresponding helpers StubHelpers::Init()
- Initialise the COM Interop layer - InitializeComInterop() (
#ifdef FEATURE_COMINTEROP
) - Initialise NDirect method calls (lazy binding of unmanaged P/Invoke targets) - NDirect::Init()
- Set-up the JIT Helper functions, so they are in place before the execution manager runs - InitJITHelpers1() and InitJITHelpers2()
- Initialise and set-up the SyncBlock cache - SyncBlockCache::Attach() and SyncBlockCache::Start()
- Create the cache used when walking/unwinding the stack - StackwalkCache::Init()
Phase 4 - Start the main components, i.e. Garbage Collector (GC), AppDomains, Security
- Start up security system, that handles Code Access Security (CAS) - Security::Start() which in turn calls SecurityPolicy::Start()
- Wire-up an event to allow synchronisation of AppDomain unloads - AppDomain::CreateADUnloadStartEvent()
- Initialise the ‘Stack Probes’ used to setup stack guards InitStackProbes() (
#ifdef FEATURE_STACK_PROBE
) - Initialise the GC and create the heaps that it uses - InitializeGarbageCollector()
- Initialise the tables used to hold the locations of pinned objects** - InitializePinHandleTable()
- Inform the debugger about the DefaultDomain, so it can interact with it - SystemDomain::System()->PublishAppDomainAndInformDebugger(..) (
#ifdef DEBUGGING_SUPPORTED
) - Initialise the existing OOB Assembly List (no idea?) - ExistingOobAssemblyList::Init() (
#ifndef FEATURE_CORECLR
) - 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
- Tell the profiler we’ve stated up - SystemDomain::NotifyProfilerStartup() (
#ifdef PROFILING_SUPPORTED
) - Pre-create a thread to handle AppDomain unloads - AppDomain::CreateADUnloadWorker() (
#ifndef CROSSGEN_COMPILE
) - Set a flag to confirm that ‘initialisation’ of the EE succeeded -
g_fEEInit = false
- Load the System Assemblies (‘mscorlib’) into the Default Domain - SystemDomain::System()->DefaultDomain()->LoadSystemAssemblies()
- 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 - Set-up the stack sampler feature, that identifies ‘hot’ methods in your code - StackSampler::Init() (
#ifdef FEATURE_STACK_SAMPLING
) - Perform any once-only SafeHandle initialization - SafeHandle::Init() (
#ifndef CROSSGEN_COMPILE
) - Set flags to indicate that the CLR has successfully started -
g_fEEStarted = TRUE
,g_EEStartupStatus = S_OK
andhr = S_OK
- 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:
- CorHost2::ExecuteAssembly()
- calling ExecuteMainMethod()
- Assembly::ExecuteMainMethod()
- calling RunMain()
- RunMain() (in assembly.cpp)
- eventually calling into you main() method
- full explanation of the ‘call’ process
在执行一行代码之前CLR做的68件事[The 68 things the CLR does before executing a single line of your code]的更多相关文章
- 安装 Kali Linux 后需要做的 20 件事
安装 Kali Linux 后需要做的 20 件事 本文含有我觉得有用的每一件事情.本文分为三大部分: 专门针对Kali用户 Kali Linux是来自Debian的一个特殊版本,Kali Linux ...
- 安装 CentOS 7 后必做的七件事
原文 安装 CentOS 7 后必做的七件事 CentOS 是最多人用来运行服务器的 Linux 版本,最新版本是 CentOS 7.当你兴趣勃勃地在一台主机或 VPS 上安装 CentOS 7 后, ...
- 刚安装Fedora 23工作站后,你必须要做的24件事
[51CTO.com快译]Fedora 23工作站版本已发布,此后我们就一直在密切关注它.我们已经为新来读者介绍了一篇安装指南:<Fedora 23工作站版本安装指南> 还有一篇介绍如何从 ...
- 新手学习SEO要做的七件事是什么?
学习SEO可能不那么先进的编程,学习SEO不可能掌握网页设计,学习SEO不需要学习SEO DIV + CSS;不是一个困难的任务,但是在学习过程中,如果你想掌握SEO,那么我们要做的几件事. 1.学习 ...
- 在 PHP 7 中不要做的 10 件事
在 PHP 7 中不要做的 10 件事 1. 不要使用 mysql_ 函数 这一天终于来了,从此你不仅仅“不应该”使用mysql_函数.PHP 7 已经把它们从核心中全部移除了,也就是说你需要迁移到好 ...
- Ubuntu装完后要做的几件事
Ubuntu装完后要做的几件事 改hosts 无论哪里,改hosts都是第一件事,没hosts咋google.没google咋活.在终端输入命令 sudo gedit /etc/hosts在# The ...
- 安装完Ubuntu 14.04要做的九件事
www.linuxidc.com/Linux/2014-04/100411.htm 1.看看有哪些新特性 安装完之后的第一件事肯定是看看Ubuntu 14.04有哪些新的特性. Ubuntu 14.0 ...
- 亲身体验:digitalocean vps能做的10件事
我写过一篇亲身体验:digitalocean和linode评测哪个好,帮助不少网友选购价格便宜性能优异的免备案vps,相信大家对两家产品有所了解.vps的性能和用途远远超过传统的虚拟主机,你拥有独立I ...
- IntelliJ IDEA安装后需要必须做的一件事
把Alt+斜杆 删除 Ctrl+空格修改成 Alt+斜杆 Ctrl+空格用过输入法的人都应该知道为什么要做上面一件事
随机推荐
- 【Django】TemplateDoesNotExist at /login/
在Django项目中配置一个简单的页面跳转 说明 OliverPro 为项目名称 ProApp 为应用程序 项目文件结构如下: 文件配置如下: 项目中的urls 应用程序urls views.py文件 ...
- 02-maven常用命令,以及使用命令创建目录
maven常用命令 mvn -v 查看maven版本 compile 编译 test 测试 package 打包 clean 删除target install 安装jar包到本地仓库中. mave ...
- 转:sock_ev——linux平台socket事件框架(基于字节流的测试程序) .
原文:http://blog.csdn.net/gdutliuyun827/article/details/8257186 由于工作与学习的需要,写了一个socket的事件处理框架,在公司写的已经使用 ...
- 使用maven拆分项目
在开发环境中,有时需要专人负责专门的模块,其他模块不需接触,这就需要将项目拆分,如下 fund_demo项目具有三个模块,现将主业务core模块单独提出另建一个项目fund_core,拆分时需要注意相 ...
- Redis集群的安装测试(伪分布模式 - 主从复制)
想跑一下Redis集群,但是没有那么多服务器,所以使用伪分布式模式,模拟一下,记录一下安装过程. 软件: redis-3.0.3.tar.gz 集群正常工作至少需要3个主节点(本示例创建6个节点,3主 ...
- Java类载入器 ClassLoader的解析
//參考 : http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 类载入器基本概念 类载入器是 Java 语言的一个创新,也是 Ja ...
- android 实现代码混淆
对于使用签名的apk,经常使用的反编译之后还是能查看class文件的代码实现.对于反编译可查看个人的博客点击打开链接 使用代码混淆就能是这样的常规反编译失效.很多其它混淆机制见官网http://dev ...
- Python3内置字符串方法详解
官网文档地址:https://docs.python.org/3/library/stdtypes.html#string-methods基于 Python 3.X 版本 str.capitalize ...
- Python 实int型和list相互转换 现把float型列表转换为int型列表 把列表中的数字由float转换为int型
第一种方法:使用map方法 >>> list = [, ] #带有float型的列表 >>> int_list = map(int,list) #使用map转换 & ...
- EF4
http://www.cnblogs.com/xray2005/category/189491.html http://kb.cnblogs.com/zt/ef/ http://www.cnblogs ...