免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5137 | 回复: 1
打印 上一主题 下一主题

[驱动] Main函数之前 v2011-10-13 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-10-13 16:31 |只看该作者 |倒序浏览
本帖最后由 crifan 于 2011-10-13 16:34 编辑

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

中找到的文件是:csu\Libc-start.c

对应函数代码为:

  1. STATIC int
  2. LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
  3.    int argc, char *__unbounded *__unbounded ubp_av,
  4. #ifdef LIBC_START_MAIN_AUXVEC_ARG
  5.    ElfW(auxv_t) *__unbounded auxvec,
  6. #endif
  7.    __typeof (main) init,
  8.    void (*fini) (void),
  9.    void (*rtld_fini) (void), void *__unbounded stack_end)
  10. {
  11. #if __BOUNDED_POINTERS__
  12.   char **argv;
  13. #else
  14. # define argv ubp_av
  15. #endif
  16.   /* Result of the 'main' function.  */
  17.   int result;
  18.   __libc_multiple_libcs = &_dl_starting_up && !_dl_starting_up;
  19. #ifndef SHARED
  20.   char *__unbounded *__unbounded ubp_ev = &ubp_av[argc + 1];
  21.   INIT_ARGV_and_ENVIRON;
  22.   /* Store the lowest stack address.  This is done in ld.so if this is
  23.      the code for the DSO.  */
  24.   __libc_stack_end = stack_end;
  25. # ifdef HAVE_AUX_VECTOR
  26.   /* First process the auxiliary vector since we need to find the
  27.      program header to locate an eventually present PT_TLS entry.  */
  28. #  ifndef LIBC_START_MAIN_AUXVEC_ARG
  29.   ElfW(auxv_t) *__unbounded auxvec;
  30.   {
  31.     char *__unbounded *__unbounded evp = ubp_ev;
  32.     while (*evp++ != NULL)
  33.       ;
  34.     auxvec = (ElfW(auxv_t) *__unbounded) evp;
  35.   }
  36. #  endif
  37.   _dl_aux_init (auxvec);
  38. # endif
  39. # ifdef DL_SYSDEP_OSCHECK
  40.   if (!__libc_multiple_libcs)
  41.     {
  42.       /* This needs to run to initiliaze _dl_osversion before TLS
  43.   setup might check it.  */
  44.       DL_SYSDEP_OSCHECK (__libc_fatal);
  45.     }
  46. # endif
  47.   /* Performe IREL{,A} relocations.  */
  48.   __libc_csu_irel ();
  49.   /* Initialize the thread library at least a bit since the libgcc
  50.      functions are using thread functions if these are available and
  51.      we need to setup errno.  */
  52.   __pthread_initialize_minimal ();
  53.   /* Set up the stack checker's canary.  */
  54.   uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
  55. # ifdef THREAD_SET_STACK_GUARD
  56.   THREAD_SET_STACK_GUARD (stack_chk_guard);
  57. # else
  58.   __stack_chk_guard = stack_chk_guard;
  59. # endif
  60. #endif
  61.   /* Register the destructor of the dynamic linker if there is any.  */
  62.   if (__builtin_expect (rtld_fini != NULL, 1))
  63.     __cxa_atexit ((void (*) (void *)) rtld_fini, NULL, NULL);
  64. #ifndef SHARED
  65.   /* Call the initializer of the libc.  This is only needed here if we
  66.      are compiling for the static library in which case we haven't
  67.      run the constructors in `_dl_start_user'.  */
  68.   __libc_init_first (argc, argv, __environ);
  69.   /* Register the destructor of the program, if any.  */
  70.   if (fini)
  71.     __cxa_atexit ((void (*) (void *)) fini, NULL, NULL);
  72.   /* Some security at this point.  Prevent starting a SUID binary where
  73.      the standard file descriptors are not opened.  We have to do this
  74.      only for statically linked applications since otherwise the dynamic
  75.      loader did the work already.  */
  76.   if (__builtin_expect (__libc_enable_secure, 0))
  77.     __libc_check_standard_fds ();
  78. #endif
  79.   /* Call the initializer of the program, if any.  */
  80. #ifdef SHARED
  81.   if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
  82.     GLRO(dl_debug_printf) ("\ninitialize program: %s\n\n", argv[0]);
  83. #endif
  84.   if (init)
  85.     (*init) (argc, argv, __environ MAIN_AUXVEC_PARAM);
  86. #ifdef SHARED
  87.   /* Auditing checkpoint: we have a new object.  */
  88.   if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
  89.     {
  90.       struct audit_ifaces *afct = GLRO(dl_audit);
  91.       struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
  92.       for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
  93. {
  94.    if (afct->preinit != NULL)
  95.      afct->preinit (&head->l_audit[cnt].cookie);
  96.    afct = afct->next;
  97. }
  98.     }
  99. #endif
  100. #ifdef SHARED
  101.   if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
  102.     GLRO(dl_debug_printf) ("\ntransferring control: %s\n\n", argv[0]);
  103. #endif
  104. #ifdef HAVE_CLEANUP_JMP_BUF
  105.   /* Memory for the cancellation buffer.  */
  106.   struct pthread_unwind_buf unwind_buf;
  107.   int not_first_call;
  108.   not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf);
  109.   if (__builtin_expect (! not_first_call, 1))
  110.     {
  111.       struct pthread *self = THREAD_SELF;
  112.       /* Store old info.  */
  113.       unwind_buf.priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf);
  114.       unwind_buf.priv.data.cleanup = THREAD_GETMEM (self, cleanup);
  115.       /* Store the new cleanup handler info.  */
  116.       THREAD_SETMEM (self, cleanup_jmp_buf, &unwind_buf);
  117.       /* Run the program.  */
  118.       result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
  119.     }
  120.   else
  121.     {
  122.       /* Remove the thread-local data.  */
  123. # ifdef SHARED
  124.       PTHFCT_CALL (ptr__nptl_deallocate_tsd, ());
  125. # else
  126.       extern void __nptl_deallocate_tsd (void) __attribute ((weak));
  127.       __nptl_deallocate_tsd ();
  128. # endif
  129.       /* One less thread.  Decrement the counter.  If it is zero we
  130.   terminate the entire process.  */
  131.       result = 0;
  132. # ifdef SHARED
  133.       unsigned int *ptr = __libc_pthread_functions.ptr_nthreads;
  134.       PTR_DEMANGLE (ptr);
  135. # else
  136.       extern unsigned int __nptl_nthreads __attribute ((weak));
  137.       unsigned int *const ptr = &__nptl_nthreads;
  138. # endif
  139.       if (! atomic_decrement_and_test (ptr))
  140. /* Not much left to do but to exit the thread, not the process.  */
  141. __exit_thread (0);
  142.     }
  143. #else
  144.   /* Nothing fancy, just call the function.  */
  145.   result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
  146. #endif
  147.   exit (result);
  148. }
复制代码


2.mainCRTStartup

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

C:\Program Files\Microsoft Visual Studio 8\VC\crt\src\crt0.c

mainCRTStartup函数代码为:

  1. __declspec(noinline)
  2. int
  3. __tmainCRTStartup(
  4. void
  5. )
  6. {
  7. unsigned int osplatform = 0;
  8. unsigned int winver = 0;
  9. unsigned int winmajor = 0;
  10. unsigned int winminor = 0;
  11. unsigned int osver = 0;
  12. int initret;
  13. int mainret=0;
  14. OSVERSIONINFOA *posvi;
  15. int managedapp;
  16. #ifdef _WINMAIN_
  17. _TUCHAR *lpszCommandLine;
  18. STARTUPINFO StartupInfo;

  19. __try {
  20. /*
  21. Note: MSDN specifically notes that GetStartupInfo returns no error, and throws unspecified SEH if it fails, so
  22. the very general exception handler below is appropriate
  23. */
  24. GetStartupInfo( &StartupInfo );
  25. } __except(EXCEPTION_EXECUTE_HANDLER) {
  26. return 255;
  27. }
  28. #endif /* _WINMAIN_ */
  29. /*
  30. * Dynamically allocate the OSVERSIONINFOA buffer, so we avoid
  31. * triggering the /GS buffer overrun detection. That can't be
  32. * used here, since the guard cookie isn't available until we
  33. * initialize it from here!
  34. */
  35. posvi = (OSVERSIONINFOA *)HeapAlloc(GetProcessHeap(), 0, sizeof(OSVERSIONINFOA));
  36. if (!posvi) {
  37. fast_error_exit(_RT_HEAP);
  38. return 255;
  39. }

  40. /*
  41. * Get the full Win32 version
  42. */
  43. posvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
  44. if ( !GetVersionExA(posvi) ) {
  45. HeapFree(GetProcessHeap(), 0, posvi);
  46. return 255;
  47. }

  48. osplatform = posvi->dwPlatformId;
  49. winmajor = posvi->dwMajorVersion;
  50. winminor = posvi->dwMinorVersion;

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

  60. _set_osplatform(osplatform);
  61. _set_winver(winver);
  62. _set_winmajor(winmajor);
  63. _set_winminor(winminor);
  64. _set_osver(osver);

  65. /*
  66. * Determine if this is a managed application
  67. */
  68. managedapp = check_managed_app();

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

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

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

  75. /*
  76. * Initialize the Runtime Checks stuff
  77. */
  78. #ifdef _RTC
  79. _RTC_Initialize();
  80. #endif /* _RTC */
  81. /*
  82. * Guard the remainder of the initialization code and the call
  83. * to user's main, or WinMain, function in a __try/__except
  84. * statement.
  85. */

  86. __try {

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

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

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

  93. if ( _tsetargv() < 0 )
  94. _amsg_exit(_RT_SPACEARG);
  95. if ( _tsetenvp() < 0 )
  96. _amsg_exit(_RT_SPACEENV);

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

  100. #ifdef _WINMAIN_


  101. lpszCommandLine = _twincmdln();
  102. mainret = _tWinMain( (HINSTANCE)&__ImageBase,
  103. NULL,
  104. lpszCommandLine,
  105. StartupInfo.dwFlags & STARTF_USESHOWWINDOW
  106. ? StartupInfo.wShowWindow
  107. : SW_SHOWDEFAULT
  108. );
  109. #else /* _WINMAIN_ */
  110. _tinitenv = _tenviron;
  111. mainret = _tmain(__argc, _targv, _tenviron);
  112. #endif /* _WINMAIN_ */

  113. if ( !managedapp )
  114. exit(mainret);

  115. _cexit();

  116. }
  117. __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
  118. {
  119. /*
  120. * Should never reach here
  121. */

  122. mainret = GetExceptionCode();

  123. if ( !managedapp )
  124. _exit(mainret);

  125. _c_exit();

  126. } /* end of try - except */

  127. return mainret;
  128. }
复制代码


【运行库】

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

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

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

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

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

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

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

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

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

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

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

【引用】

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

论坛徽章:
0
2 [报告]
发表于 2011-10-16 18:11 |只看该作者
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP