在C中,main()方法最初是如何调用的?

时间:2010-08-12 16:39:34

标签: c operating-system

C程序如何开始?

7 个答案:

答案 0 :(得分:44)

操作系统调用main()函数。实际上,它通常会调用其他名为_init之类的奇怪事物。 C编译器将标准库链接到提供此操作系统定义的入口点的每个应用程序,然后调用main()

编辑:显然,对某些人来说,这不够详细和正确。

许多Unix OS使用的Executable and Linkable Format (ELF)定义了一个入口点地址。这是程序在操作系统完成exec()调用后开始运行的地方。在Linux系统上,这是_init。

来自objdump -d:

Disassembly of section .init:

08049f08 <_init>:
 8049f08:       55                      push   %ebp
 8049f09:       89 e5                   mov    %esp,%ebp
 8049f0b:       83 ec 08                sub    $0x8,%esp
 8049f0e:       e8 a1 05 00 00          call   804a4b4 <call_gmon_start>
 8049f13:       e8 f8 05 00 00          call   804a510 <frame_dummy>
 8049f18:       e8 d3 50 00 00          call   804eff0 <__do_global_ctors_aux>
 8049f1d:       c9                      leave  
 8049f1e:       c3                      ret    

来自readelf -d:

 0x00000001 (NEEDED)                     Shared library: [libstdc++.so.6]
 0x00000001 (NEEDED)                     Shared library: [libm.so.6]
 0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]
 0x00000001 (NEEDED)                     Shared library: [libpthread.so.0]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8049f08
 0x0000000d (FINI)                       0x804f018
 0x00000004 (HASH)                       0x8048168
 0x00000005 (STRTAB)                     0x8048d8c
 0x00000006 (SYMTAB)                     0x804867c
 0x0000000a (STRSZ)                      3313 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x8059114
 0x00000002 (PLTRELSZ)                   688 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8049c58
 0x00000011 (REL)                        0x8049be0
 0x00000012 (RELSZ)                      120 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x8049b60
 0x6fffffff (VERNEEDNUM)                 3
 0x6ffffff0 (VERSYM)                     0x8049a7e
 0x00000000 (NULL)                       0x0

您可以看到INIT等于_init的地址。

frame_dummy和__do_global_ctors_aux的代码位于一组名为crtbegin.o和crtend.o的文件中(以及这些名称的变体)。这些是GCC的一部分。该代码为C程序做了各种必要的事情,比如设置stdin,stdout,全局和静态变量等等。

以下文章很好地描述了它在Linux中的作用(摘自下面的答案,投票少):http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html

我相信其他人的回答已经描述了Windows的作用。

答案 1 :(得分:24)

最终它是操作系统。通常在实际入口点和主要功能之间存在一些介质,这是由编译器链接器插入的。

一些细节(与Windows相关):PE文件中有一个名为IMAGE_OPTIONAL_HEADER的标题,其中包含字段AddressOfEntryPoint,该字段又是将要执行的文件中第一个代码字节的地址

答案 2 :(得分:9)

答案 3 :(得分:6)

操作系统调用main。可重定位的可执行文件中将有一个指向main位置的地址(有关更多信息,请参阅Unix ABI)。

但是,谁打电话给操作系统?

“RESET”信号上的中央处理单元(在通电时也会断言)将开始在给定地址(例如,0xffff)的某些ROM中查找其指令。

通常会向BIOS发出某种跳转指令,它会配置内存芯片,加载基本硬盘驱动程序等等。然后读取硬盘驱动器的引导扇区,并且 next 启动引导程序,它会加载包含如何读取NTFS分区以及如何读取内核文件本身的基本信息的文件。将设置内核环境,加载内核,然后 - 然后! - 内核将跳转到执行。

在完成所有艰苦工作之后,内核可以继续加载我们的软件。

答案 4 :(得分:5)

操作系统调用C运行时(CRT)中包含的函数并链接到您的可执行文件中。称之为“CRT main。”

CRT main做了一些事情,其中​​最重要的两件事,至少在C ++中,是运行一系列全局C ++类并调用它们的构造函数,并调用你的main()函数并给出它的返回值到壳。

如果内存服务,Visual C ++ CRT main会做更多的事情。它配置内存分配器,如果使用Debug CRT来帮助查找内存泄漏或错误访问,这一点非常重要。它还在structured exception处理程序中调用main来捕获错误的内存访问和其他崩溃并显示它们。

答案 5 :(得分:4)

请注意,除了已发布的答案外,您还可以自己致电main。通常,这是为混淆代码保留的坏主意。

答案 6 :(得分:4)

您的问题的最佳信息可能在下面提到的链接中找到 http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html, 迄今为止我遇到的最好的一个。