From double Click to Main: The initialization of Process in OS

         从双击一个应用程序到生成一个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

 

posted on 2008-12-07 22:03  lbq1221119  阅读(1927)  评论(5编辑  收藏  举报

导航