最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

Main函数之前 v2011-10-13

工作和技术 crifan 2564浏览 0评论

Main函数之前

Version: 2011-10-13

Author: green-waste (at)163.com

 

【声明】

此文主要是摘录和整理《程序员修养》那本书中关于运行库部分相关的内容,为了简要的总结一下main函数之前都干了啥。

 

【正文】

多数人接触C语言的人,对main函数,应该都比较了解了。不少人,应该是,或许以为main就是程序执行的入口,或许知道main函数不是入口,但是不知道哪里是入口,又或许知道入口,但是对于main之前发生的事情,系统做了哪些事情,不是很清楚。

那么,下面这段文字,目的就是简单介绍一下,main之前到底发生了什么。

 

main函数中,一般来说,你可以放心去使用很多资源了,比如:申请内存,使用系统调用,触发异常,访问I/O,使用scan/printf等输入输出等等。

之所以你可以放心使用这些资源,那么是因为有人帮你在main函数执行之前,就帮你准备好了这些东西供你使用。

到底是谁做的?答案是,运行库。不过在介绍运行库之前,先要清楚一般程序执行的过程是怎么样的。

 

【程序运行步骤】

一个典型的程序运行步骤大致如下:

(1)       操作系统在创建进程后,把控制权交到了程序的入口,这个入口往往是运行库的某个入口函数。

(2)       入口函数对运行库和程序运行环境进行初始化,包括堆HeapI/O,线程。全局变量构造等等。

(3)       入口函数在完成初始化之后,调用main函数,正式执行程序主体部分。

(4)       Main函数执行完毕之后,返回到入口函数,入口函数进行清理工作,包括全局变量析构、堆销毁、关闭I/O等,然后进行系统调用结束进程。

 

【入口函数】

从上面可以看出,其实真正所谓的程序开始部分,其实可以算是入口函数。

入口函数,也叫入口点(Entry Point),是运行库的一部分,视平台不同而有不同名字。

 

下表显示了不同平台下,对应的入口函数的相关内容:

表格 1 不同平台下的入口函数相关信息

平台

运行库

入口函数的名称

说明

Linux

Glibc.c

__libc_start_main

详情参考glibc源码中libc/csu位置的源码

Windows

MSVC CRTCrt0.c

mainCRTStartup

Microsoft Visual Studio 2003为例,代码位于VC安装目录crt/src

 

【__libc_start_main和mainCRTStartup】

1. __libc_start_main

Glib的库源码

http://ftp.gnu.org/gnu/libc/glibc-2.14.tar.bz2

中找到的文件是:csuLibc-start.c

对应函数代码为:

 STATIC int
LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
   int argc, char *__unbounded *__unbounded ubp_av,
#ifdef LIBC_START_MAIN_AUXVEC_ARG
   ElfW(auxv_t) *__unbounded auxvec,
#endif
   __typeof (main) init,
   void (*fini) (void),
   void (*rtld_fini) (void), void *__unbounded stack_end)
{
#if __BOUNDED_POINTERS__
  char **argv;
#else
# define argv ubp_av
#endif

  /* Result of the ‘main’ function.  */
  int result;

  __libc_multiple_libcs = &_dl_starting_up && !_dl_starting_up;

#ifndef SHARED
  char *__unbounded *__unbounded ubp_ev = &ubp_av[argc + 1];

  INIT_ARGV_and_ENVIRON;

  /* Store the lowest stack address.  This is done in ld.so if this is
     the code for the DSO.  */
  __libc_stack_end = stack_end;

# ifdef HAVE_AUX_VECTOR
  /* First process the auxiliary vector since we need to find the
     program header to locate an eventually present PT_TLS entry.  */
#  ifndef LIBC_START_MAIN_AUXVEC_ARG
  ElfW(auxv_t) *__unbounded auxvec;
  {
    char *__unbounded *__unbounded evp = ubp_ev;
    while (*evp++ != NULL)
      ;
    auxvec = (ElfW(auxv_t) *__unbounded) evp;
  }
#  endif
  _dl_aux_init (auxvec);
# endif
# ifdef DL_SYSDEP_OSCHECK
  if (!__libc_multiple_libcs)
    {
      /* This needs to run to initiliaze _dl_osversion before TLS
  setup might check it.  */
      DL_SYSDEP_OSCHECK (__libc_fatal);
    }
# endif

  /* Performe IREL{,A} relocations.  */
  __libc_csu_irel ();

  /* Initialize the thread library at least a bit since the libgcc
     functions are using thread functions if these are available and
     we need to setup errno.  */
  __pthread_initialize_minimal ();

  /* Set up the stack checker’s canary.  */
  uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
# ifdef THREAD_SET_STACK_GUARD
  THREAD_SET_STACK_GUARD (stack_chk_guard);
# else
  __stack_chk_guard = stack_chk_guard;
# endif
#endif

  /* Register the destructor of the dynamic linker if there is any.  */
  if (__builtin_expect (rtld_fini != NULL, 1))
    __cxa_atexit ((void (*) (void *)) rtld_fini, NULL, NULL);

#ifndef SHARED
  /* Call the initializer of the libc.  This is only needed here if we
     are compiling for the static library in which case we haven’t
     run the constructors in `_dl_start_user’.  */
  __libc_init_first (argc, argv, __environ);

  /* Register the destructor of the program, if any.  */
  if (fini)
    __cxa_atexit ((void (*) (void *)) fini, NULL, NULL);

  /* Some security at this point.  Prevent starting a SUID binary where
     the standard file descriptors are not opened.  We have to do this
     only for statically linked applications since otherwise the dynamic
     loader did the work already.  */
  if (__builtin_expect (__libc_enable_secure, 0))
    __libc_check_standard_fds ();
#endif

  /* Call the initializer of the program, if any.  */
#ifdef SHARED
  if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
    GLRO(dl_debug_printf) (“ninitialize program: %snn”, argv[0]);
#endif
  if (init)
    (*init) (argc, argv, __environ MAIN_AUXVEC_PARAM);

#ifdef SHARED
  /* Auditing checkpoint: we have a new object.  */
  if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
    {
      struct audit_ifaces *afct = GLRO(dl_audit);
      struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
 {
   if (afct->preinit != NULL)
     afct->preinit (&head->l_audit[cnt].cookie);

   afct = afct->next;
 }
    }
#endif

#ifdef SHARED
  if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
    GLRO(dl_debug_printf) (“ntransferring control: %snn”, argv[0]);
#endif

#ifdef HAVE_CLEANUP_JMP_BUF
  /* Memory for the cancellation buffer.  */
  struct pthread_unwind_buf unwind_buf;

  int not_first_call;
  not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf);
  if (__builtin_expect (! not_first_call, 1))
    {
      struct pthread *self = THREAD_SELF;

      /* Store old info.  */
      unwind_buf.priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf);
      unwind_buf.priv.data.cleanup = THREAD_GETMEM (self, cleanup);

      /* Store the new cleanup handler info.  */
      THREAD_SETMEM (self, cleanup_jmp_buf, &unwind_buf);

      /* Run the program.  */
      result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
    }
  else
    {
      /* Remove the thread-local data.  */
# ifdef SHARED
      PTHFCT_CALL (ptr__nptl_deallocate_tsd, ());
# else
      extern void __nptl_deallocate_tsd (void) __attribute ((weak));
      __nptl_deallocate_tsd ();
# endif

      /* One less thread.  Decrement the counter.  If it is zero we
  terminate the entire process.  */
      result = 0;
# ifdef SHARED
      unsigned int *ptr = __libc_pthread_functions.ptr_nthreads;
      PTR_DEMANGLE (ptr);
# else
      extern unsigned int __nptl_nthreads __attribute ((weak));
      unsigned int *const ptr = &__nptl_nthreads;
# endif

      if (! atomic_decrement_and_test (ptr))
 /* Not much left to do but to exit the thread, not the process.  */
 __exit_thread (0);
    }
#else
  /* Nothing fancy, just call the function.  */
  result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
#endif

  exit (result);
}

   

 

2.mainCRTStartup

此处安装的VS2005,对应的文件为:

C:Program FilesMicrosoft Visual Studio 8VCcrtsrccrt0.c

 mainCRTStartup函数代码为:

 __declspec(noinline)
int
__tmainCRTStartup(
         void
         )
{
        unsigned int osplatform = 0;
        unsigned int winver = 0;
        unsigned int winmajor = 0;
        unsigned int winminor = 0;
        unsigned int osver = 0;
        int initret;
        int mainret=0;
        OSVERSIONINFOA *posvi;
        int managedapp;
#ifdef _WINMAIN_
        _TUCHAR *lpszCommandLine;
        STARTUPINFO StartupInfo;

        __try {
                        /*
                        Note: MSDN specifically notes that GetStartupInfo returns no error, and throws unspecified SEH if it fails, so
                        the very general exception handler below is appropriate
                        */
            GetStartupInfo( &StartupInfo );
        } __except(EXCEPTION_EXECUTE_HANDLER) {
            return 255;
        }
#endif  /* _WINMAIN_ */
        /*
         * Dynamically allocate the OSVERSIONINFOA buffer, so we avoid
         * triggering the /GS buffer overrun detection.  That can’t be
         * used here, since the guard cookie isn’t available until we
         * initialize it from here!
         */
        posvi = (OSVERSIONINFOA *)HeapAlloc(GetProcessHeap(), 0, sizeof(OSVERSIONINFOA));
        if (!posvi) {
            fast_error_exit(_RT_HEAP);
            return 255;
        }

        /*
         * Get the full Win32 version
         */
        posvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
        if ( !GetVersionExA(posvi) ) {
            HeapFree(GetProcessHeap(), 0, posvi);
            return 255;
        }

        osplatform = posvi->dwPlatformId;
        winmajor = posvi->dwMajorVersion;
        winminor = posvi->dwMinorVersion;

        /*
         * The somewhat bizarre calculations of _osver and _winver are
         * required for backward compatibility (used to use GetVersion)
         */
        osver = (posvi->dwBuildNumber) & 0x07fff;
        HeapFree(GetProcessHeap(), 0, posvi);
        if ( osplatform != VER_PLATFORM_WIN32_NT )
            osver |= 0x08000;
        winver = (winmajor << 8) + winminor;

        _set_osplatform(osplatform);
        _set_winver(winver);
        _set_winmajor(winmajor);
        _set_winminor(winminor);
        _set_osver(osver);

        /*
         * Determine if this is a managed application
         */
        managedapp = check_managed_app();

        if ( !_heap_init(1) )               /* initialize heap */
            fast_error_exit(_RT_HEAPINIT);  /* write message and die */

        if( !_mtinit() )                    /* initialize multi-thread */
            fast_error_exit(_RT_THREAD);    /* write message and die */

        /* Enable buffer count checking if linking against static lib */
        _CrtSetCheckCount(TRUE);

        /*
         * Initialize the Runtime Checks stuff
         */
#ifdef _RTC
        _RTC_Initialize();
#endif  /* _RTC */
        /*
         * Guard the remainder of the initialization code and the call
         * to user’s main, or WinMain, function in a __try/__except
         * statement.
         */

        __try {

            if ( _ioinit() < 0 )            /* initialize lowio */
                _amsg_exit(_RT_LOWIOINIT);

            /* get wide cmd line info */
            _tcmdln = (_TSCHAR *)GetCommandLineT();

            /* get wide environ info */
            _tenvptr = (_TSCHAR *)GetEnvironmentStringsT();

            if ( _tsetargv() < 0 )
                _amsg_exit(_RT_SPACEARG);
            if ( _tsetenvp() < 0 )
                _amsg_exit(_RT_SPACEENV);

            initret = _cinit(TRUE);                  /* do C data initialize */
            if (initret != 0)
                _amsg_exit(initret);

#ifdef _WINMAIN_

            lpszCommandLine = _twincmdln();
            mainret = _tWinMain( (HINSTANCE)&__ImageBase,
                                 NULL,
                                 lpszCommandLine,
                                 StartupInfo.dwFlags & STARTF_USESHOWWINDOW
                                      ? StartupInfo.wShowWindow
                                      : SW_SHOWDEFAULT
                                );
#else  /* _WINMAIN_ */
            _tinitenv = _tenviron;
            mainret = _tmain(__argc, _targv, _tenviron);
#endif  /* _WINMAIN_ */

            if ( !managedapp )
                exit(mainret);

            _cexit();

        }
        __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
        {
            /*
             * Should never reach here
             */

            mainret = GetExceptionCode();

            if ( !managedapp )
                _exit(mainret);

            _c_exit();

        } /* end of try – except */

        return mainret;
}

    

【运行库】

接下来,再简单解释解释运行库。

运行库,即运行时库(Runtime Library),而C语言的运行库,即被称为C运行库(CRTC Runtime Language)。

最初看到这个缩写,我以为是CRT显示器呢,呵呵。

 

一个C语言运行库大致包含了如下功能:

(1)       启动与退出:包括入口函数及入口函数所依赖的其他函数等。

(2)       标准函数:由C语言标准规定的C语言标准库所拥有的函数实现。

(3)       I/OI/O功能的封装和实现。

(4)       堆:堆的封装和实现。

(5)       语言实现:语言中一些特殊的功能。

(6)       调试:实现调试功能的代码。

 

关于每一部分的详细内容,就不多说了。

 

【引用】

1.《程序员的自我修养 链接、装载与库》 俞甲子 石凡 潘爱民

 

转载请注明:在路上 » Main函数之前 v2011-10-13

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
87 queries in 0.224 seconds, using 22.22MB memory