惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
Stack Overflow Blog
Stack Overflow Blog
人人都是产品经理
人人都是产品经理
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
Vercel News
Vercel News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
博客园 - 司徒正美
B
Blog RSS Feed
Microsoft Azure Blog
Microsoft Azure Blog
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
L
LangChain Blog

博客园 - lbq1221119

时间同步算法与Simple Ring-based election algorithm算法分析 SSCLI中GC垃圾回收源码分析(3) - GarbageCollectGeneration()与SuspendEE SSCLI中GC垃圾回收源码分析(2) - GarbageCollect()与Spin Lock SSCLI中GC源码分析(1) - EE与BCL之间的调用接口FCall (北京宇思信德科技公司)诚聘C#软件工程师 , Middle及Senior Level .Net Micro framework在开发过程中的bugs/problems及解决方案。 MF中使用GPRS:如何通过一个串口终端实现GPRS Modem拨号上网 博客园北京俱乐部第三次活动讲义PPT ValueType.Equals(null)的底层实现及CLR虚拟机对其结构支持 WebService传输数据流及数据交互解析 托管线程退出之后Dump文件特征 国外精品研究论文(持续更新) Random()中具体实现(含种子数组的实现) From double Click to Main: PAL initialization - lbq1221119 Microsoft Micro Framework 3.0对Serial Peripheral Interface 的支持 使用CorDbg进行托管调试 CLR内核调试之:Malloc函数实现 Build SSCLI20 under VS2008 full Document (完全手册) MethodTable内存空间分配中加法运算算法解析
From double Click to Main: The initialization of Process ...
lbq1221119 · 2008-12-07 · via 博客园 - lbq1221119

         从双击一个应用程序到生成一个OSprocess,然后到执行这个exe文件的Main方法,中间做了些什么呢?经常看着call stack的这块灰色的地方,总是想看看其是如何实现的。

         Processcreation分为两个级别的创建:NT级别的和Windows级别的。CreateProcess这个方法,就会进行这两个级别的创建,同时创建Threads。俺们找到

c:\Program Files\Microsoft SDKs\Windows\v6.0A\Include\WinBase.h,来查看关于这个部分的定义:

#ifdef UNICODE

#define CreateProcess  CreateProcessW

#else

#define CreateProcess  CreateProcessA

#endif // !UNICODE

如果定义了UNICODE,就使用CreateProcessW方法来创建,反之对于ANSI的系统,就用CreateProcessA方法来创建,这是一个thin shim。可以看看CreateProcessW的定义方法:

WINBASEAPI

BOOL

WINAPI

CreateProcessW(

    __in_opt    LPCWSTR lpApplicationName,

    __inout_opt LPWSTR lpCommandLine,

    __in_opt    LPSECURITY_ATTRIBUTES lpProcessAttributes,

    __in_opt    LPSECURITY_ATTRIBUTES lpThreadAttributes,

    __in        BOOL bInheritHandles,

    __in        DWORD dwCreationFlags,

    __in_opt    LPVOID lpEnvironment,

    __in_opt    LPCWSTR lpCurrentDirectory,

    __in        LPSTARTUPINFOW lpStartupInfo,

    __out       LPPROCESS_INFORMATION lpProcessInformation

    );

         CreateProcess的目标,就是留下一个APCqueue。这个queue里面保存了new process里面需要初始化的thread。这里WinExecLoadModule还是被支持的,只是调用的还是CreateProcess这个方法。

         可以使用下面的代码来创建一个Process

         STARTUPINFO si;

    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );

    si.cb = sizeof(si);

    ZeroMemory( &pi, sizeof(pi) );

    // Start the child process.

    if( !CreateProcess( NULL,   // No module name (use command line)

        argv[1],        // Command line

        NULL,           // Process handle not inheritable

        NULL,           // Thread handle not inheritable

        FALSE,          // Set handle inheritance to FALSE

        0,              // No creation flags

        NULL,           // Use parent's environment block

        NULL,           // Use parent's starting directory

        &si,            // Pointer to STARTUPINFO structure

        &pi )           // Pointer to PROCESS_INFORMATION structure

    )

    {

        printf( "CreateProcess failed (%d)\n", GetLastError() );

        return;

    }

    // Wait until child process exits.

    WaitForSingleObject( pi.hProcess, INFINITE );

    // Close process and thread handles.

    CloseHandle( pi.hProcess );

    CloseHandle( pi.hThread );

首先看看Clix启动的时候的Call Stack

 

看看BaseProcessStart启动的时候是做了些什么:

VOID

BaseProcessStart( PPROCESS_START_ROUTINE lpStartAddress )

{

    try {

                   // Set the state of Thread Object.

        NtSetInformationThread( NtCurrentThread(),

                                ThreadQuerySetWin32StartAddress,

                                &lpStartAddress,

                                sizeof( lpStartAddress )

                              );

        ExitThread((lpStartAddress)());

        }

    except(UnhandledExceptionFilter( GetExceptionInformation() )) {

        if ( !BaseRunningInServerProcess ) {

            ExitProcess(GetExceptionCode());

            }

        else {

            ExitThread(GetExceptionCode());

            }

        }

}

NtSetInformationThread主要是设定启动ProcessThread的一些状态信息。可以在反编译中看到这个函数的信息:

_BaseProcessStart@4:

  push        0Ch 

  push        7C816FE0h

  call        __SEH_prolog (7C8024C6h)

  and         dword ptr [ebp-4],0

  push        4   

  lea         eax,[ebp+8]

  push        eax 

  push        9   

  push        0FFFFFFFEh

  call        dword ptr [__imp__NtSetInformationThread@16 (7C8013A0h)]

  call        dword ptr [ebp+8]

  push        eax  //Break Point

  call        _ExitThread@4 (7C80C058h)

  nop             

ok,这个时候设置好了启动线程之后,就引导到了crtexe.c__tmainCRTStartup()函数中了。

是如何从KernerBaseProcessStart跳转到tmainCRTStartup的呢?位于两个不同的dll中。我们写的C/C++的程序,运行的时候需要库文件的支持,再启动一个程序的时候,链接器会首先找到程序的启动函数:

如果建立了一个console的程序,编译器的开关可能是下面的形式:

/subsystem:"console" /entry:"mainCRTStartup" (ANSI)
/subsystem:"console" /entry:"wmainCRTStartuup" (UNICODE)

现在,再mainCRTStartup中,调用了下列方法和函数:

#ifdef WPRFLAG

int wmainCRTStartup(

#else  /* WPRFLAG */

int mainCRTStartup(

#endif  /* WPRFLAG */

#endif  /* _WINMAIN_ */

        void

        )

{

        /*

         * The /GS security cookie must be initialized before any exception

         * handling targetting the current image is registered.  No function

         * using exception handling can be called in the current image until

         * after __security_init_cookie has been called.

         */

        __security_init_cookie();

        return __tmainCRTStartup();

}

这个函数中,也是做了一个转换,真正的Main函数调用再__tmainCRTStartup方法实现中的:

#ifdef WPRFLAG

            __winitenv = envp;

            mainret = wmain(argc, argv, envp);

#else  /* WPRFLAG */

            __initenv = envp;

            mainret = main(argc, argv, envp);

#endif  /* WPRFLAG */

下面一句的调用,就transfer到了d:\Rotor\sscli20\palrt\inc\palstartup.h的对RotorPAL层操作上面:

#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

         对启动部分的分析,就到这个地方,很多涉及到选择exe入口函数的link选项,结合#pragma指令还有许多中配置方法。

         下一篇从PAL的初始化讲起,一直到执行应用程序的Main方法。

         Sunday, December 07, 2008 9:46:49 PM

         Lbq1221119@cnblogs.com first post at sscli.cnblogs.com