• ----:)欢迎访问源码网(:----
    • 首页
    • 博客
    • 学院
    • 下载
    • 论坛
    • 影视
    • 发布源码
    • RSS
    • ITPig
    • 笑话网
    • 百家姓
    • 繁體中文

源码网 - 中国第一源码门户
选择镜像:网通镜像 - 电信主站
  • 首 页
  • 新闻动态
  • 网站运营
  • 网页制作
  • WEB开发
  • 编程开发
  • 图像媒体
  • 操作系统
  • 数据库
  • 服务器
热门搜索 优化 SEO 故事 cms IIS7 MySQL 个人 AdSense 主题推广 | 文章搜索: 高级搜索
会员登录/控制面版您的位置: 学院首页 >> WEB开发 >> .NET 开发 >> 详细内容
 

推荐文章

 
 

热点文章

  • FckEditor远程图片下载插件
  • TFS(Team Foundation Server)使用经验
  • IIS过滤器实现.NET程序不破解DLL替换字符串一法
  • 为ASP.NET封装的SQL数据库访问类
  • ASP.NET2.0中文验证码的实现
  • Url地址重写,利用HttpHander手工编译页面并按需生成静..
  • ASP.NET学习笔记一——ASP和ASP.NET比较
  • 使用HtmlInputHidden 控件在本页面保持状态和跨页面传..
  • ASP.Net发邮件
  • Silverlight 2.0中文学习资源集萃
  • WinForm中使用XtraGrid控件,实现在界面中动态修改列显..
  • 解析ASP.NET木马文件操作
 
 

相关文章

  • Rotor实现中用到的Macro
  • .NET/Rotor源码研究1补遗 - 解决无法检测操作系统版本..
  • .NET / Rotor源码分析5 - 开始使用WinDbg+SOS调试,ss..
  • .NET / Rotor源码分析4 - 修改Rotor使其发送CLR Notif..
  • .NET / Rotor源码研究3 – 调试Rotor托管代码的利器:W..
  • .NET / Rotor 源码研究1 - Building Rot
  • 谈.NET中几个怪异的CustomAttribute
  • .NET Main函数参数解释过程和特殊规则
  • .NET SDK中CorFlags.Exe的用法
  • .NET中的幕后英雄:MSCOREE.DLL
  • .NET Interop: 从IErrorInfo错误对象获得托管代码的异..
  • .NET牛人应该知道些什么
 
 

百度搜索

 
 

.NET / Rotor 源码研究2 – PAL Initialization / Termination

  • 阅览次数:
  • 文章来源: http://blog.csdn.net/atfield
  • 原文作者: atfield
  • 整理日期: 2008-07-19
  • 发表评论
  • 字体大小:
  • 小
  • 中
  • 大

1.     Introduction

PAL (Platform Adaptation Layer) 是Rotor得以在多种平台之间移植的关键所在。不同操作系统的API区别非常大,很难简单用#if/#else/#endif之类的语句进行条件编译来解决不同操作系统API之间互不兼容的问题,Rotor的解决方法是创建一个兼容层PAL,建立在PAL之上的程序无需关心面对的是何种操作系统,只需调用PAL提供的服务即可,在底层,PAL调用被转换成相应的操作系统相关的API调用,便可以解决此类问题。如果需要支持其他操作系统,只需为此操作系统写一个PAL实现,无需重写所有代码。Rotor的PAL设计和Win32 API十分接近,是Win32 API的一个子集,因此Win32 PAL的实现相对于*NIX版的PAL要简单很多,而对于*NIX来说,由于操作系统设计和Win32 API非常不同,需要做大量工作。PAL在架构上并无特别之处,只是简单的C函数,根据动态连接库实现的不同而不同,并没有使用通常C++所常用的Factory方式。由于篇幅和个人精力有限,以后文章的的讨论只限于Win32版本PAL实现。
PAL的主要组成部分如下:
DLL
Description
rotor_pal.dll
和平台相关的PAL的实现
rotor_palrt.dll
PAL Runtime,和平台无关的公共代码都在这里。比如加密/解密,资源,Assert,String等等
rotor_palrt_s.dll
静态GUID的定义
 
2.     PAL_Initialization & PAL_Termination
PAL的Initialization/Termination是通过PAL_Initialize/PAL_Terminate来完成的。PAL_Initialize/PAL_Terminate只是PAL_Startup & PAL_Shutdown的一层Thin Wrapper,其主要作用在于维护一个PalReferenceCount,避免对PAL_Startup/PAL_Shutdown的重复调用。为了并发安全性,PalReferenceCount的++/--是通过InterlockedIncrement/InterlockedDecrement进行的。PAL_Initialize代码如下:
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);
 
 
    }
    LOGAPI("PAL_Initialize(argc=0x%x, argv=%p)\n", argc, argv);
 
 
    LOGAPI("PAL_Initialize returns int 0x%x\n", RetVal);
    return RetVal;
}
 
PAL_Startup负责初始化PAL:
1.       首先PAL_Startup获得rotor_pal.dll的FullPathName:
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;
    }
2.       设置ErrorMode,从而在无法打开文件的和CriticalError的情况下不显示错误对话框,直接返回错误。
3.       调用RegisterWithEventLog在注册表中注册Rotor,这样RegisterEventSource才可以注册Rotor这个Source,ReportEvent才能工作。具体请参看MSDN。
4.       调用InitializeObjecNameMangler初始化NameMangler。NameMangler的作用是根据rotor_pal.dll的所在位置用SHA1算法计算出一个Hash值,再转换为一个合法路径名。在很多需要名字的地方,如CreateEvent, CreateMutex…等均会将传入的名字进行Mangle处理,从而使得这些名字在不同的rotor_pal.dll实例中互不冲突。详情下面会有一节专门讲述。
    SetErrorMode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS);
 
    RegisterWithEventLog(ModulePath);
 
    if (!InitializeObjectNameMangler(ModulePath)) {
        return RetVal;
    }
5.       接下来,仅当在调试版本中,根据PAL_API_TRACING的值设置LogFileHandle打开对PAL API的跟踪输出。PAL_API_TRACING 可以是stdout, stderr, debugger或者指定文件名,PAL通过PalLogApi, PalLogApiCore, PalAssertFired函数来调用WriteFile(LogFileHandle)或OutputDebugString(当PAL_API_TRACING的值为debugger时)来输出TRACE的内容。
#if DBG
        //
        // Do one-time initialization of the API tracing mechanism
        //
    {
        DWORD dw;
        WCHAR buffer[_MAX_PATH];
 
        dw = GetEnvironmentVariableW(L"PAL_API_TRACING",
                                     buffer,
                                     sizeof(buffer)/sizeof(WCHAR));
        if (dw != 0 && dw < sizeof(buffer)/sizeof(WCHAR)) {
            // Environment variable exists and fit in the buffer
            if (wcscmp(buffer, L"stdout") == 0) {
                LogFileHandle = GetStdHandle(STD_OUTPUT_HANDLE);
            } else if (wcscmp(buffer, L"stderr") == 0) {
                LogFileHandle = GetStdHandle(STD_ERROR_HANDLE);
            } else if (wcscmp(buffer, L"debugger") == 0) {
                LogFileHandle = 0;
            } else {
                LogFileHandle = CreateFileW(buffer,
                                            GENERIC_WRITE,
                                            FILE_SHARE_READ,
                                            NULL,
                                            CREATE_ALWAYS,
                                            FILE_ATTRIBUTE_NORMAL,
                                            NULL);
            }
        }
    }
#endif //DBG
 
6.       之后,安装PAL自己的Exception Filter,这样PAL便可以接管对Unhandled Exceptions的处理,下篇文章会详细解释
7.       获得当前线程一个TLS(Thread Local Storage)的Slot,被SEH用到,同样,下篇文章会详细讲到
8.       检查PAL_TRY_LOCAL_SIZE,在下篇文章会解释
9.       设置浮点运算的控制字(Control Word)
    // 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;
}
 
 
PAL_Shutdown则是Shutdown PAL:
1.       释放NameManager所占用的内存和资源
2.       释放SEH Tls所占用的TLS 的Slot,恢复之前的Exception Filter
3.       在调试版本中,关闭LogFileHandle
void
PALAPI
PAL_Shutdown(
              void)
{
    PAL_LPTOP_LEVEL_EXCEPTION_FILTER PreviousFilter;
 
    if (hCryptProv != NULL) {
        CryptReleaseContext(hCryptProv, 0);
        hCryptProv = NULL;
    }
 
    free(NameManglerA);
    NameManglerA = NULL;
    free(NameManglerW);
    NameManglerW = NULL;
 
 
    if (SEH_Tls != TLS_OUT_OF_INDEXES) {
        TlsFree(SEH_Tls);
    }
 
    // Reset the global exception filter
    PreviousFilter = (PAL_LPTOP_LEVEL_EXCEPTION_FILTER)
        SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)SEH_CurrentTopLevelFilter);
 
#if DBG
    // Do this after the LOGAPI, so the output gets logged
    if (LogFileHandle != INVALID_HANDLE_VALUE) {
        //
        // Refcount is zero and logging is enabled - close the
        // file handle now in preparation for unload.
        //
        if (LogFileHandle) {
            CloseHandle(LogFileHandle);
        }
        LogFileHandle = INVALID_HANDLE_VALUE;
    }
#endif
}
 
 
3.     Application Startup
下面以clix (Managed Application Launcher)为例,说明用到了PAL的程序的启动过程:
由于clix引用了rotor_pal.dll和rotor_palrt.dll,在clix的main执行之前,rotor_pal.dll和rotor_palrt.dll的DllMain会相继执行:
首先是rotor_pal.dll的DllMain被执行,当Dll在进程中被Load的时候 (DLL_PROCESS_ATTACH) 记录Dll的HINSTANCE,并调用DisableThreadLibraryCalls避免DllMain在线程创建 (DLL_THREAD_ATTACH) 和销毁 (DLL_THREAD_DETACH) 的时候被调用以提高速度:
// This is the main DLL entrypoint
BOOL
WINAPI
DllMain(
        HANDLE hDllHandle,
        DWORD dwReason,
        LPVOID lpReserved)
{
    if (dwReason == DLL_PROCESS_ATTACH) {
        hInstPal = (HINSTANCE)hDllHandle;
        DisableThreadLibraryCalls(hDllHandle);
    }
    return TRUE;
}
之后,rotor_palrt.dll的DllMain被执行:
1.       DLL_PROCESS_ATTACH的时候
a.       调用ROTOR_PAL_CTOR_TEST_RUN验证静态构造函数已经在DllMain之前执行,估计是部分编译器并没有遵循标准,有此宏可以更加容易定位此类错误。可参考rotor_palrt.h中ROTOR_PAL_CTOR_BODY和ROTOR_PAL_CTOR_TEST_RUN的定义
b.      调用PAL_Initialize,这是对PAL_Initialize的第一次调用,会调用PAL_Startup。注意argc和argv均传了NULL,这是因为在Win32的PAL实现中,PAL_Initialize不需要argc和argv。由于Unix/Linux实现下需要用到argc/argv来初始化一些信息,这些信息只能由外部传入得到,因此PAL_Initialize函数还是需要argc/argv这两个参数。
c.       为APP_DATA指针分配一个TLS (Thread Local Storage)的Slot g_itlsAppData, 这个Slot对于任何一个Thread都是一样的。APP_DATA的作用是保存CNumInfo的指针(和Thread相关的数字表示的设置,和culture对应起来)和IErrorInfo接口指针(记录错误信息)。
d.      调用InitNumInfo(TRUE),参数为TRUE表明是做初始化,FALSE表明则是释放,两种情况下InitNumInfo做的事情不同,TRUE则初始化CrticialSection,在CNumInfo内部用到。FALSE则释放CriticalSection和CNumInfo中的一些缓存信息,具体可以参考palrt\src\convert.cpp
extern "C"
BOOL WINAPI DllMain( HINSTANCE hInst, DWORD dwReason, LPVOID pvReserved )
{
 
    switch (dwReason)
    {
        case DLL_PROCESS_ATTACH:
            ROTOR_PAL_CTOR_TEST_RUN(ROTOR_PALRT);
            // initialize PAL
            if (PAL_Initialize(NULL, NULL) != 0)
                return FALSE;
 
            // initialize APP_DATA
            if ((g_itlsAppData = TlsAlloc()) == ITLS_EMPTY) {
                return FALSE;
            }
            InitNumInfo(TRUE);
            break;
2.       DLL_PROCESS_DETACH
a.       调用ReleaseAppData释放g_itlsAppData对应的TLS的Slot中保存的APP_DATA指针
b.      调用InitNumInfo释放CNumInfo中的Cache
c.       调用PAL_Terminate
3.       DLL_THREAD_DETACH的时候,调用ReleaseAppData释放和此线程相关的APP_DATA。
 
        case DLL_PROCESS_DETACH:
            ReleaseAppData();
            InitNumInfo(FALSE);
            if (g_itlsAppData != ITLS_EMPTY) {
                TlsFree(g_itlsAppData);
            }
 
            PAL_Terminate();
            break;
 
        case DLL_THREAD_ATTACH:
            break;
 
        case DLL_THREAD_DETACH:
            ReleaseAppData();
            break;
    }
    return TRUE;
}
在rotor_pal.dll和rotor_palrt.dll的DllMain被执行完毕之后,palrt/palstartup.h中的main被调用。注意clix.cpp中的main并不是真正的main,main已经被定义成了PAL_startup_main,而真正的main则是在palstartup.h之中定义的:
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
 
    // PAL_Terminate is a stdcall function, but it takes no parameters
    // so the difference doesn't matter.
    atexit((void (__cdecl *)(void)) PAL_Terminate);
 
    mainargs.argc = argc;
    mainargs.argv = argv;
 
 
    exit((int)PAL_EntryPoint((PTHREAD_START_ROUTINE)run_main, &mainargs));
    return 0;   // Quiet a compiler warning
}
main作了如下几件事情:
1.初始化PAL
2.调用atexit注册退出函数PAL_Terminate,这样PAL_Terminate便会在程序正常结束的时候被调用。注意虽然PAL_Terminate和atexit所要求的函数调用协定不同,PAL_Terminate是__stdcall而atexit参数要求__cdecl,由于PAL_Terminate是无参数的,__stdcall/__pascal在无参数的时候是没有区别的,因此可以直接强制转换成__cdecl
3.调用PAL_EntryPoint入口函数,此函数调用run_main,并最后调用PAL_startup_main函数。如上所述,PAL_startup_main才是clix中的main
4.退出时候,自动调用PAL_Terminate
OK,这次就分析到这里。下篇文章会重点分析PAL中的Exception机制。

上一篇:PHP使用zlib扩展实现页面GZIP压缩输出
下一篇:构建支持Master/Slave读写分离的数据库操作类
  • 网友评论:
  • 查看所有评论
  • 我要发表评论
您的网名:
留言主题:
你要发表的内容:

 

关于本站 | 广告联系 | 版权声明 | 网站地图 | 发布软件 | 帮助中心 | 源码论坛

Copyright © 2005-2007 CodePub.Com  程序支持:木翼  滇ICP备05005971号