c - loader 和 C 运行时初始化的作用区别

标签 c runtime loader

我正在从以下链接阅读 C 运行时初始化的作用:http://www.embecosm.com/appnotes/ean9/html/ch05s02.html

它说运行时初始化执行诸如设置堆栈之类的任务,并且在更多页面中详细说明它还说它用零初始化 bss 段。在其他一些地方,我还读到它初始化数据和其他一些段。

这让我对加载程序做了什么产生了怀疑?因为其中一些任务也是 loader 的职责。

所以,我的问题:

  • 运行时初始化或 c 运行时实际上做什么?
  • loader 实际上是做什么的?

  • 编辑

    好的,如果该链接具体描述了嵌入式系统的运行时初始化的作用,那么它在普通系统上的作用是什么。
    就我而言,运行时初始化将只调用 main 并且没有其他工作要做。

    最佳答案

    1. What does the runtime initialization or c runtime actually do?


    维基百科定义了一个 runtime library作为:

    a set of low-level routines used by a compiler to invoke some of the behaviors of a runtime environment, by inserting calls to the runtime library into compiled executable binary.



    对于 C 程序,运行时库除了 bootstrap 之外几乎没有什么可做的。编译器调用 C 运行时来引导各种环境事物,然后基本上通过调用 main 将控制权交给用户。 .

    鉴于您的问题评论中的回答,您可能已经发现,针对其环境 bootstrap 的过程随目标环境的数量而变化。考虑到 C 现在和过去支持的平台和操作系统的数量,没有办法枚举 C 运行时已经工作或当前工作的所有方式。

    每个 C 库都有自己的 C 运行时,每个支持 C 的环境都可能有不同的引导问题和要求。这些要求在很大程度上取决于操作系统或硬件的特性以及 C 实现的完整性。但是,我可以回答一些您可能熟悉的环境中 C 运行时通常会做的事情。
  • 由于 C 运行时负责调用 main ,因此调用通过 atexit(3) 注册的函数将是 C 运行时的责任。
  • 解析并调用任何构造函数/析构函数接口(interface)( _init_fini 等)
  • 初始化并调用实时加载器(它负责解析和加载在链接时注册并在运行时加载的动态共享对象)。
  • 优雅地处理分离线程的退出。
  • argc的初始化和传递和 argv进入程序的main .
  • 定义和初始化各种 C 库全局符号。例如,它设置 errno适合环境(现代系统将 errno 定义为线程安全的,因此它需要存在于 TLS 中)。 environ是另一个需要在调用 main 之前初始化的全局符号.
  • 就此而言,C 运行时需要设置 TLS。
  • 吨更多。

  • 您可能有兴趣浏览 glibc implementation of the runtime ,在“csu”(C 启动)目录中找到。 (此目录之外有一些特定于机器的部分。)

    不同的系统会有不同的要求。正如您所读到的,嵌入式系统可能有更多的运行时工作,因为它们可能负责从寄存器初始化到程序加载和执行的任务(这不是由任何内核提供的)。考虑到嵌入式目标上足够复杂的独立项目,“C 运行时”和“内核”之间的区别可能会变得模糊。

    现在:

    1. What does [a] loader actually do?


    加载器有很多种,也取决于运行时环境。对于带有 EEPROM 的小型嵌入式环境,加载程序可能是一些固件,它开始执行它在地址 0 找到的任何内容。您也可以将自己视为加载程序,手动将二进制文件写入 EEPROM。

    在现代操作系统中,有许多加载程序。
  • 引导加载程序。从历史上看,这些操作的方式是 BIOS 选择一个引导设备,查看地址,将 512 字节的数据读入内存,然后从那里开始执行。我已经离开这个世界有一段时间了,所以我不确定 EFI/UEFI 有什么区别,除了它们足够完整(和复杂)的引导环境。
  • 内核。当你执行一个程序时,会在幕后进行大量的工作来让它运行。假设您在某些类 Unix 操作系统中从 shell 运行程序,加载过程可能如下所示:
  • 您的 shell 尝试在您的环境配置中的某处查找二进制文件 PATH .这是通过向内核发出多个系统调用以解析不同路径序列下的文件名来完成的。
  • 假设找到文件,shell 通常会 fork(2)execve(2) . fork(2)调用使内核创建一个新进程; execve(2) call 用新的二进制文件替换克隆的二进制文件。
  • 内核从其存储介质(磁盘、网络、内存等)读取文件的第一页,并试图找出如何执行它。
  • 如果它是一个 ELF 二进制文件,它可以从二进制文件的 header 中确定。然后,内核根据 ELF 部分 header 中指定的偏移量将二进制文件的部分加载到内存中的某处,为堆栈等设置映射区域,然后根据入口地址(也是 ELF header 的一部分)开始执行。这个入口点大概是_start ,C 运行时的一部分。
  • 如果它不是 ELF 二进制文件,它仍然可以通过解释器执行。内核将尝试从文件的开头(例如 #!/bin/bash )解析解释器,解析它并执行它。最终它会找到一个 ELF 可执行文件,否则它将失败。
  • 内核开始执行二进制文件,可能在 _start , 如前所述。
  • Eli Bendersky 对此有更详尽的文章,标题为“How statically linked programs run on linux”。
  • 运行时加载器/动态链接器/任何你想称呼它们的东西。我将向您推荐“Anatomy of Linux dynamic libraries”文章以了解有关这些工作原理的信息。当然,dlopen(3)/dlsym(3)/dlclose(3)/dlerror(3)一组函数只是一个用于与动态加载器交互的 API。我强烈建议阅读这些接口(interface)的手册页,以很好地了解 Linux 动态加载器支持的功能集,以及加载器的功能。
  • 关于c - loader 和 C 运行时初始化的作用区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27596818/

    相关文章:

    c - gtk+2.0 哪个小部件用于好友列表?

    c -/proc的内核模块

    java - 确定从 Java 程序执行进程的运行时系统

    ruby - 有没有办法检查 Ruby 中方法代码的内容,甚至修改它?

    c++ - Linux 链接器/加载器的环境覆盖

    c - 从 txt.file 读取链接列表,需要帮助更正代码

    c - 如何在 C/glib 中将二进制字符串转换为十六进制字符串?

    c - 为什么在这个程序中运行时错误取决于输入的值?

    javascript - 如何使用 Angular js 显示和 ajax 加载器 gif

    app-store-connect - iTunes Connect 应用程序缺少所需的架构