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