一、前言
众所周知,c程序是从main函数开始执行的。在unix-like系统中,我们使用c语言写了一个应用程序,然后编译源代码,链接,生成可执行文件,最后只要运行可执行文件,我们的程序就从main函数开始执行了。不知道大家有没有想过,为什么系统知道要从main函数开始运行呢?这其实就是启动例程的作用。
本节将介绍什么是unix系统的启动例程以及启动例程的作用。
二、什么是启动例程?
对单片机有过了解的朋友都应该知道,在运行main函数前,需要先运行一个.S的汇编程序,用于初始化内存空间,栈空间等,然后调用main函数,这个其实就是一个启动例程。只不过单片机一般只跑一个应用程序,也就只有一个main。
unix系统其实也是类似的,在运行应用程序的main函数之前,需要先初始化好main函数的运行环境,然后调用main函数,在main函数结束后进行清理和退出工作。这就是unix系统中应用程序的启动例程所做的工作。
无论是单片机还是unix系统,启动例程一般都是由汇编来写的,如果用c语言来表达的话类似如下形式:
void _start() {
// 初始化C运行库
__libc_init();
// 调用main函数
int result = main(argc, argv);
// 清理和退出
exit(result);
}
如果main函数有返回值的话,其返回值会作为程序的结束码传给exit().
三、启动例程是由谁编写的?
在单片机中,启动例程一般是由芯片原厂编写的,因为其具有唯一性。但在unix系统中,每一个可执行程序都有一个启动例程,其当然不可能是由芯片厂写的。
unix系统中的启动例程是由c运行库提供的,其生成和链接的过程如下:
编译阶段:编译器将源代码文件(如C或C++文件)编译成目标文件(通常是.o文件)。在这个阶段,编译器不会生成启动例程,而是生成与源代码对应的机器码。
链接阶段:链接器将编译生成的目标文件与C运行库中的启动例程以及其他必要的库文件链接在一起,生成最终的可执行文件。启动例程(如_start函数)是由C运行库提供的,链接器会将这个启动例程链接到最终的可执行文件中,作为程序的入口点。
总结:可执行文件 = 启动例程 + 源码编译生成的目标文件 + 链接库