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 阅读(1489) 评论(0) 编辑 收藏 举报