From double Click to Main: PAL initialization
在上一篇文章,講到了雙擊一個應用程序之后,操作系統如何初始化Process,以及創建相關的context,最后引導到應用程序的Main方法中。
在托管代碼中,對于Main的啟動還有點不同,有一個PAL層在啟動main方法之前啟動:
#ifdef __cplusplus
extern "C"
#endif
int __cdecl main(int argc, char **argv) {
struct _mainargs mainargs;
#ifdef _MSC_VER
if (PAL_Initialize(0, NULL)) {
return 1;
}
#else
if (PAL_Initialize(argc, argv)) {
return 1;
}
#endif
可以參考上篇文章里面的call stack,找到對main(int argc, char **argv)方法的調用。_MSC_VER是預編譯控制,標識microsoft的C編譯器的版本。
DotNet中,對于PAL層的initialization和terminal,是通過PAL_Initialize/PAL_Terminate兩個方法來完成的,打開d:\Rotor\sscli20\pal\win32\win32pal.c就可以看到:
PALIMPORT int PALAPI PAL_Initialize(int argc,char *argv[])
{
int RetVal = 0;
LONG RefCount;
RefCount = InterlockedIncrement(&PalReferenceCount);
if (RefCount == 1) {
RetVal = PAL_Startup(argc, argv);//Responsible for Start.
}
return RetVal;
}
PALIMPORT void PALAPI PAL_Terminate(void)
{
LONG RefCount;
LOGAPI("PAL_Terminate()\n");
RefCount = InterlockedDecrement(&PalReferenceCount);
//
// if this hits, the host has a refcounting bug - the
// count has underflowed. This is not a PAL bug.
//
PALASSERT(RefCount >= 0);
if (RefCount == 0) {
PAL_Shutdown();//Responsible for terminate
}
}
這里可以看出,PAL_Initialize和PAL_Terminate只是負責PAL Startup和shotdown的一個轉換的函數,在PAL_Initializae中,維護一個LONG PalReferenceCount;這個變量主要是為了避免對PAL初始化或者shutdown的重復調用,使用c:\Program Files\Microsoft SDKs\Windows\v6.0A\Include\WinBase.h中的兩個函數來維護這個PalReferenceCount:
WINBASEAPI
LONG
WINAPI
InterlockedIncrement (
__inout LONG volatile *lpAddend
);
WINBASEAPI
LONG
WINAPI
InterlockedDecrement (
__inout LONG volatile *lpAddend
);
然后通過執行RetVal = PAL_Startup(argc, argv)來啟動PAL:
Int PALAPI PAL_Startup(int argc,char *argv[])
{
int RetVal = -1;
HMODULE hMod;
WCHAR ModulePath[_MAX_PATH];
hMod = GetModuleHandleW(L"rotor_pal.dll");
if (!hMod) {
return RetVal;
}
if (!GetModuleFileNameW(hMod,ModulePath,
sizeof(ModulePath)/sizeof(WCHAR))) {
return RetVal;
}
SetErrorMode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS);
這里,首先加載rotor_pal.dll,然后獲取這個DLL文件的ModulePath。同時設置錯誤模式。
RegisterWithEventLog(ModulePath);
if (!InitializeObjectNameMangler(ModulePath)) {
return RetVal;
}
// Note: MSVCRT has already registered their exception filter at this point
// SEH_CurrentTopLevelFilter won't be NULL
SEH_CurrentTopLevelFilter = (PAL_LPTOP_LEVEL_EXCEPTION_FILTER) SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)PAL_ExceptionFilter);
if ((SEH_Tls = TlsAlloc()) == TLS_OUT_OF_INDEXES) {
goto LExit;
}
PALASSERT(PAL_TRY_LOCAL_SIZE >= sizeof(jmp_buf));
// exceptions masked, round to nearest, 53 bit precision.
_controlfp(_MCW_EM | _RC_NEAR | _PC_53 | _IC_PROJECTIVE | _DN_SAVE,
_MCW_EM | _MCW_RC | _MCW_PC | _MCW_IC | _MCW_DN);
RetVal = 0;
LExit:
return RetVal;
}
RegisterWithEventLog(ModulePath);
記錄EventLog的source,被忽略掉的錯誤信息,以防這個程序是non-admin權限的。如果沒有注冊成功的話,這個信息同樣會被保留,但是在Event Viewer里面,就會以“The description for Event ID ( 0 ) in Source ( Rotor ) cannot be found.”開頭。記錄到:
SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\Rotor
InitializeObjectNameMangler(ModulePath)所做的工作,就是初始化一個Object Name的Mangler,使用了OS提供的SHA1加密算法,來得到一個hash過后的字符串。這就會使用在不同的Rotor實例中,rotor_pal.dll不會沖突。
接下來,在調試版本中,會初始化API tracing機制,首先獲取到PAL_API_TRACING的值,然后設置對不同的API的tracing。
SEH_CurrentTopLevelFilter = (PAL_LPTOP_LEVEL_EXCEPTION_FILTER) SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)PAL_ExceptionFilter);
這里,注冊安裝PAL自己的TOP exception handle。因為這個地方MSVCRT已經注冊了一個Exception handle,所以這個地方的SEH_CurrentTopLevelFilter不會為空。這樣注冊之后,就可以由PAL自己來處理Unhandled Exception。
這里也是調用了OS的Base API:
WINBASEAPI
LPTOP_LEVEL_EXCEPTION_FILTER
WINAPI
SetUnhandledExceptionFilter(
__in LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
if ((SEH_Tls = TlsAlloc()) == TLS_OUT_OF_INDEXES) {
goto LExit;
}
獲取當前線程的一個TLS的SLOT,暫時不清楚哪里會用到。
PALASSERT(PAL_TRY_LOCAL_SIZE >= sizeof(jmp_buf));
保證PAL_TRY_LOCAL_SIZE會大于一個獲取程序狀態的緩沖區。
// exceptions masked, round to nearest, 53 bit precision.
_controlfp(_MCW_EM | _RC_NEAR | _PC_53 | _IC_PROJECTIVE | _DN_SAVE,
_MCW_EM | _MCW_RC | _MCW_PC | _MCW_IC | _MCW_DN);
設置浮點數的控制字,讀寫浮點控制或者狀態字時候用的,managed code不支持,所以這里需要手工設置。
OK,到這里PAL啟動完畢,下面一章看看如何啟動EE并且launch應用程序的Main函數的。
Tuesday, December 09, 2008
lbq1221119@bj first post at sscli.cnblogs.com
posted on 2008-12-09 21:12 lbq1221119 閱讀(1501) 評論(0) 收藏 舉報
浙公網安備 33010602011771號