前言
.Net執行模型,無非就兩個過程。一個是呼叫入口函式,另外一個就是編譯入口函式。前者主呼叫,後者主編譯。
概括
一:入口函式:RunMainInternal
所有的.Net程式,包括控制檯,Web,窗體等等入口都是Main。而呼叫這個Main的就是RunMainInternal,從這個函式的名字就可以看出,它的Run就表示是執行Main函式的,而後面的Internal表示是內部的,而非外部所能呼叫,當然你用一些非常規手段,這個函式還是可以外部的呼叫的,那是後話。
二:透過LLDB看下它的堆疊
* thread #1, name = 'corerun', stop reason = step over
* frame #0: 0x00007ffff6d53cdf libcoreclr.so`RunMainInternal
frame #1: 0x00007ffff6d53c99 libcoreclr.so`RunMain
frame #2: 0x00007ffff6d4ec76 libcoreclr.so`RunMain
frame #3: 0x00007ffff6d4ea22 libcoreclr.so`RunMain
frame #4: 0x00007ffff6d4f030 libcoreclr.so`Assembly::ExecuteMainMethod
frame #5: 0x00007ffff6dc2b9c libcoreclr.so`CorHost2::ExecuteAssembly
frame #6: 0x00007ffff6d1a6c1 libcoreclr.so`::coreclr_execute_assembly
frame #7: 0x000055555556f9f9 corerun`run(config=0x00007fffffffdbc0)
frame #9: 0x00007ffff7829d90 libc.so.6`__libc_start_call_main
frame #10: 0x00007ffff7829e40 libc.so.6`__libc_start_main_impl
frame #11: 0x000055555556c0e5 corerun`_start + 37
可以看到它在Linux平臺下面,是透過Glibc OR Newlibc呼叫的。而它的前面一個函式就是RunMain,這個透過它名字也可以看到它就是執行Main函式的。
三:編譯函式PreStubWorker
這個函式顧名思義,預樁工作。就是插樁的意思。每次編譯一個函式,都會經過它。它會呼叫RyuJIT把函式編譯成機器碼,然後再返回到這個被編譯函式的函式頭地址執行。
部分程式碼:
extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, MethodDesc* pMD)
{
PCODE pbRetVal = NULL;
BEGIN_PRESERVE_LAST_ERROR;
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_MODE_ANY;
STATIC_CONTRACT_ENTRY_POINT;
// 此處省略一萬字
}
可以看到它只是帶了一個MethodDesc,也就是函式的描述結構體。透過它可以找到Module,MSIL,然後進行一個編譯。
四:看下它的堆疊
thread #1, name = 'corerun', stop reason = breakpoint 2.1
* frame #0: 0x00007ffff6ef55d0 libcoreclr.so`::PreStubWorker
frame #2: 0x00007ffff731e14f libcoreclr.so`CallDescrWorkerInternal
frame #3: 0x00007ffff6faae98 libcoreclr.so`CallDescrWorkerWithHandler
frame #4: 0x00007ffff6fabb8d libcoreclr.so`MethodDescCallSite::CallTargetWorker
frame #5: 0x00007ffff6d2e4b3 libcoreclr.so`MethodDescCallSite::Call
frame #6: 0x00007ffff6d53f6f libcoreclr.so`RunMainInternal
frame #7: 0x00007ffff6d53c99 libcoreclr.so`RunMain
frame #8: 0x00007ffff6d4ec76 libcoreclr.so`RunMain
frame #9: 0x00007ffff6d4ea22 libcoreclr.so`RunMain
frame #10: 0x00007ffff6d4f030 libcoreclr.so`Assembly::ExecuteMainMethod
frame #11: 0x00007ffff6dc2b9c libcoreclr.so`CorHost2::ExecuteAssembly
frame #12: 0x00007ffff6d1a6c1 libcoreclr.so`::coreclr_execute_assembly
frame #13: 0x000055555556f9f9 corerun`run(config=0x00007fffffffdbc0) argv=0x00007fffffffddb8) at corerun.cpp:624:21
frame #15: 0x00007ffff7829d90 libc.so.6`__libc_start_call_main
frame #16: 0x00007ffff7829e40 libc.so.6`__libc_start_main_impl
frame #17: 0x000055555556c0e5 corerun`_start + 37
它剛好在RunMainInternal的後面,說明它是在被呼叫之後,才會進行編譯。
結尾
作者:江湖評談