本文主要讲述在Linux x86-64中,静态链接程序(即不使用任何共享库(shared libaray)的程序)的运行过程。
序章
C/C++程序员在写程序时,总是默认程序是从main
函数开始的,我们会认为这理所当然,但事实上,当程序在执行到main
函数时,很多事情已经完成了。我们可以看看一下几个例子:
#include <stdio.h>
int a = 10;
int main(int argc, const char * argv[]) {
printf("%d\n", argc);
printf("%d\n", a);
return 0;
}
在运行main函数的时候,全局变量a
已经初始化完成,并且main
的两个参数argc
与argv
已经被传了进来。
#include <stdio.h>
__attribute((constructor)) void before_main()
{
printf("%s\n",__FUNCTION__);
}
__attribute((destructor)) void after_main()
{
printf("%s\n",__FUNCTION__);
}
int main(int argc, const char * argv[]) {
return 0;
}
执行结果:
before_main
main
after_main
构造函数before_main
会在main
函数开始之前被调用,析构函数after_main
会在main
函数结束之后被调用。
而C++中,main
函数之前所能执行的代码还会更多。所以,main
函数既不是一个程序的开始,也不是一个程序的结束。那么,一个程序到底是怎样开始和结束的呢,main
函数前后到底发生了那些事呢?这就是我们要讨论的话题。
Linux内核装载ELF过程
当我们在Linux系统的bash下输入一个命令执行某个ELF程序时,bash进程会调用fork
系统调用创建一个新的进程,然后新的进程会调用exec
系统调用执行指定的ELF文件。
![](https://img.haomeiwen.com/i3915558/575614b34194b714.png)
内核通过exec
运行一个进程,在C start-up routine
中,系统会自动调用一些初始化函数,再执行main
函数,然后通过调用exit
,先执行那些通过atexit
注册的函数,再进行一些收尾工作,最后使用_exit
结束进程。
要想进一步了解,可以阅读以下资料:
网友评论