C启动程序需要什么?

C启动程序需要什么?,c,exec,startup,main,C,Exec,Startup,Main,引用其中一本unix编程书籍 当一个C程序由 kernelby,exec函数之一 调用特殊的启动例程。这 函数在主函数之前调用 函数被调用。可执行文件 程序文件将此例程指定为 程序的起始地址; 这是由链接编辑器在以下情况下设置的: 它由C编译器调用。这 启动例程从 内核命令行参数和 环境和设置的事情是这样的 主函数被称为 如前所示 为什么我们需要一个中间人启动程序。exec函数可以直接调用main函数,内核可以直接将命令行参数和环境传递给main函数。为什么我们需要介于两者之间的启动例程?启动例

引用其中一本unix编程书籍

当一个C程序由 kernelby,exec函数之一 调用特殊的
启动例程
。这 函数在主函数之前调用 函数被调用。可执行文件 程序文件将此例程指定为 程序的起始地址; 这是由链接编辑器在以下情况下设置的: 它由C编译器调用。这 启动例程从 内核命令行参数和 环境和设置的事情是这样的 主函数被称为 如前所示


为什么我们需要一个中间人
启动程序
。exec函数可以直接调用main函数,内核可以直接将命令行参数和环境传递给main函数。为什么我们需要介于两者之间的启动例程?

启动例程初始化CRT(即创建CRT堆,以便
malloc
/
空闲
工作,初始化标准i/O流等);在C++的情况下,它也调用全局变量的构造函数。可能还有其他特定于系统的设置,您应该检查运行时库的源以了解更多详细信息。

因为C没有“插件”的概念。因此,如果您想使用,比如说,
malloc()
必须有人初始化必要的数据结构。C程序员很懒,不想一直写这样的代码:

main() {
    initialize_malloc();
    initialize_stdio();
    initialize_...();
    initialize_...();
    initialize_...();
    initialize_...();
    initialize_...();

    ... oh wow, can we start already? ...
}
因此,C编译器会找出需要做什么,生成必要的代码并设置所有内容,以便您可以立即开始使用代码。

调用
main()
是C语言的事情,而调用
\u start()
是内核的事情,由二进制格式头中的入口点指示。(为了清楚起见:内核不希望或不需要知道我们称之为
\u 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!

还需要知道的重要一点是,应用程序是在用户模式下执行的,任何系统都会调用、设置特权位并进入内核模式。这有助于提高操作系统的安全性,因为它可以防止用户访问内核级系统调用和大量其他复杂操作。因此,对printf的调用将捕获、设置内核模式位、执行代码,然后重置为用户模式并返回到应用程序


需要CRT来帮助您并允许您在Windows和Linux中使用所需的语言。它为操作系统提供了一些非常基本的引导功能,为您的开发提供了功能集。

当然您不“需要”它,它只是一种方便。您能告诉我它提供了什么方便吗?非常好。我越来越有知识了。非常感谢:)@LinuxPenseur:也可以看看我刚才添加的示例并尝试一下!如果没有退出(0)
它将发出SIGSEGV,您能否解释一下为什么会发生这种情况?