程序真的是从main开始的吗?

程序真的是从main开始的吗?

程序从main开始的吗?

在执行main之前全局变量已经初始化,main函数的两个参数也被正确传了进来,堆和栈的初始化也已经完成,一些系统I/O也被初始化。

完成上面这些工作的函数称为入口函数(Entry Point)。一个典型的运行步骤大致如下:

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

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

·入口函数在完成初始化之后,调用main函数,正是开始执行程序主体部分

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


Glibc的入口函数:

_start

在调用_start前,装载器把用户参数和环境变量压入栈中,按照其压栈的方法,实际上栈顶的元素是argc,而接着其下就是argc和环境变量数组。

_start大概的功能可以用下面的代码描述:

void _start()

{

  %ebp = 0;

  int argc = pop from stack

  char ** argv = top of stack;

  __libc_start_main(main, argc, argv, __libc_csu_init, __linc_csu_fini,

  edx, top of stack);

}

其中argv除了指向参数表之外,还隐含紧接着环境变量表。这个环境变量表在__libc_start_main里从argv内部提取出来。


MSVC的入口函数:

int  mainCRTStartup(void)

{

  ...

}

在该函数中使用了alloca进行内存分配,这是因为堆还没有初始化,而alloca是唯一可以不使用堆的动态分配机制的函数。

alloca可以再栈上分配任曦大小的空间(只要栈允许),并且在函数放回的时候自动释放,好像局部变量一样。

mainCRTStartup 的总体流程就是:

      1.初始化和OS版本有关的全局变量

  2.初始化堆

  3.初始化I/O

  4.获取命令行参数和环境变量

  5.初始化C库的一些数据

  6.调用main并记录返回值

  7.检查错误并将main的返回值返回

 

MSVC CRT 的入口函数初始化

MSVC的入口函数初始化主要包含两部分,堆初始化和I/O初始化。MSVC的对初始化由函数_heap_init完成(调用HeapCreate)。

 

I/O初始化工作比较复杂,主要进行如下几个工作:

·建立打开的文件表

·如果能够继承自父进程,那么从父进程获取继承的句柄

·初始化标准输入输出

 

C语言运行库(C Runtime Library):

C运行库大致包含如下功能:

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

·标准函数:由C语言标准规定的C语言标准库所拥有的函数实现

·I/O:I/O功能的封装和实现

·堆:堆的封装和实现

·语言实现:语言中的一些特殊功能的实现

·调试:实现调试功能的代码


参考:《程序员的自我修养》


 


你可能感兴趣的:(程序真的是从main开始的吗?)