引用其中一个unix编程书籍,
当C程序被执行时 kernelby,exec函数之一 调用特殊
start-up routine
。这个 函数在main之前调用 函数被调用。可执行文件 程序文件将此例程指定为 该计划的起始地址; 这是由链接编辑器设置的 它由C编译器调用。这个 启动例程从中获取值 内核命令行参数和 环境和设置如此 主函数被称为 如前所示。
为什么我们需要一个中间人start-up routine
。 exec函数可以直接调用main函数,内核可以直接将命令行参数和环境传递给main函数。为什么我们需要中间的启动程序?
答案 0 :(得分:9)
因为C没有“插件”的概念。因此,如果你想使用,malloc()
有人必须初始化必要的数据结构。 C程序员很懒,不想一直写这样的代码:
main() {
initialize_malloc();
initialize_stdio();
initialize_...();
initialize_...();
initialize_...();
initialize_...();
initialize_...();
... oh wow, can we start already? ...
}
因此,C编译器会确定需要完成的工作,生成必要的代码并设置所有内容,以便您可以立即开始使用 代码。
答案 1 :(得分:5)
启动例程初始化CRT(即创建CRT堆以使malloc
/ free
工作,初始化标准I / O流等);在C ++的情况下,它还调用全局变量的构造函数。可能还有其他特定于系统的设置,您应该检查运行时库的源代码以获取更多详细信息。
答案 2 :(得分:5)
调用main()
是一个C事物,而调用_start()
是一个内核事物,由二进制格式头中的入口点表示。 (为清楚起见:内核不希望或不需要知道我们称之为_start
)
如果你有一个非C二进制文件,你可能没有main()
函数,你可能根本就没有“函数”的概念。
所以实际问题是:为什么编译器不将main()
的地址作为起点?那是因为典型的libc实现想要在真正启动程序之前进行一些初始化,请参阅其他答案。
编辑作为示例,您可以像这样更改输入点:
$ cat entrypoint.c
int blabla() { printf("Yes it works!\n"); exit(0); }
int main() { printf("not called\n"); }
$ gcc entrypoint.c -e blabla
$ ./a.out
Yes it works!
答案 3 :(得分:0)
重要的是要知道应用程序是在用户模式下执行的,任何系统都会调出,设置特权位并进入内核模式。这有助于防止用户访问内核级系统调用以及无数其他复杂情况,从而提高操作系统的安全性。因此,对printf的调用将陷阱,设置内核模式位,执行代码,然后重置为用户模式并返回到您的应用程序。
CRT需要帮助您并允许您在Windows和Linux中使用所需的语言。它为操作系统提供了一些非常基础的引导,为您提供开发功能集。