【问题标题】:When a binary file runs, does it copy its entire binary data into memory at once? Could I change that?当一个二进制文件运行时,它是否一次将其整个二进制数据复制到内存中?我可以改变吗?
【发布时间】:2012-01-20 08:50:19
【问题描述】:

它会在执行之前将整个二进制文件复制到内存中吗?我对这个问题很感兴趣,想把它改成其他方式。我的意思是,如果二进制文件有 100M 大(似乎不可能),我可以在将其复制到内存时运行它。这可能吗?

或者您能告诉我如何查看它的运行方式吗?我需要哪些工具?

【问题讨论】:

  • 操作系统通常会以理智的方式处理这个问题。
  • 100MB 二进制不可能?我这里有一个 6MB 的二进制文件,它是从一个只有 500 个 LOC、大量模板化和 Boosted C++ 程序编译而来的。我敢打赌,我可以毫不费力地将其增加到 100MB。
  • 没有什么是不可能的!只需与快乐的 Eclipse 开发人员联系,他们会教你所有技巧。

标签: c linux binary


【解决方案1】:

应用程序级程序员的理论模型表明情况确实如此。其实正常的启动过程(至少在Linux 1.x中,我相信2.x和3.x都做了优化但差不多)是:

  • 内核创建一个进程上下文(或多或少,虚拟机)
  • 到该进程上下文中,它定义了一个虚拟内存映射,该映射映射 从 RAM 地址到可执行文件的开头
  • 假设您是动态链接的(默认/通常),ld.so 程序 (例如/lib/ld-linux.so.2)在程序的头文件中定义为共享库设置内存映射
  • 内核在程序的启动例程中执行jmp(对于 C 程序,即 类似crtprec80,它调用main)。由于它只设置了映射,并没有实际加载任何页面(*),这会导致 CPU 的内存管理单元出现页面错误,这是对内核的中断(异常、信号)。
  • 内核的页面错误处理程序加载程序的某些部分,包括部分 导致页面错误的内容写入 RAM。
  • 当您的程序运行时,如果它访问一个没有 RAM 支持的虚拟地址 立即启动,将发生页面错误并导致内核暂停程序 简单地说,从光盘加载页面,然后将控制权返回给程序。这一切 发生在“指令之间”,通常无法检测到。
  • 当您使用 malloc/new 时,内核会创建 RAM 的读写页面(没有磁盘备份文件)并将它们添加到您的虚拟地址空间。
  • 如果您通过尝试访问在虚拟内存映射中设置的内存位置引发页面错误,则会收到分段违规信号 (SIGSEGV),这通常是致命的。
  • 当系统用完物理 RAM 时,RAM 页面会被删除;如果它们是磁盘上已有内容的只读副本(如可执行文件或共享对象文件),它们只会被解除分配并从源重新加载;如果它们是可读写的(就像您使用 malloc “创建”的内存一样),它们会被写入(页面文件 = 交换文件 = 交换分区 = 磁盘虚拟内存)。访问这些“已释放”的页面会导致另一个页面错误,并且它们会被重新加载。

不过,一般来说,在您的进程大于可用 RAM 之前 - 并且数据几乎总是比可执行文件大得多 - 您可以放心地假装您在世界上是孤独的,并且没有发生这种需求分页的事情。

所以:实际上,内核已经正在在你的程序被加载时运行它(并且可能永远不会加载某些页面,如果你从不跳转到该代码/引用该数据)。 p>

如果您的启动特别缓慢,您可以查看prelink 系统来优化共享库负载。这减少了ld.so 在启动时(在您的程序的exec 和被调用的main 之间以及在您第一次调用库例程时)必须做的工作量。

有时,静态链接可以提高程序的性能,但会大量消耗 RAM — 由于您的库不是共享的,因此除了共享的 libc 之外,您还要复制“您的 libc”,每个例如,其他程序正在使用。这通常只在您的程序或多或少单独在机器上运行的嵌入式系统中有用。

(*) 其实内核更聪明一点,一般会预加载一些页面 减少页面错误的数量,但理论是一样的,不管 优化

【讨论】:

  • 我感觉你忘记了需要在加载到内存中的可执行块上运行的重定位器 (en.wikipedia.org/wiki/Relocation_%28computer_science%29)。据我所知,这在加载代码期间发生过一次,我不确定“页面错误”处理程序是否会在程序已经运行时简单地运行这个“重定位器”算法。所以在我看来,一个可执行块至少必须在执行开始之前加载一次。但我不是“linux-loader”专家……
  • 相信在Linux上这一切都由ld.so处理,但我不倾向于插手巫师的事情:-)……还有很多黑人thunk 操作等所涉及的魔法,在第一次调用库例程时发生……
  • 请参阅em386.blogspot.com/2006/10/…,了解有关搬迁工作原理的一些讨论。
  • 这强化了我的模糊理解,即延迟链接过程在代码运行时发生……内核实际上可以mmap 二进制文件并在任何符号通过 PLT 实际链接之前开始执行它。通过虚拟机上的启动过程来跟踪 '(kernel dynamic-linker user-program libc) 的实际执行可能是“有趣的”,因为“有趣”的定义很无聊,但可以写出好的研究论文。
  • @BRPocock 等:能否请您指出,装载机(或搬迁装载机)在哪里出现?内核何时开始从可执行文件创建进程上下文?
【解决方案2】:

不,它只会将必要的页面加载到内存中。这是请求分页。

我不知道有什么工具可以真正实时显示,但您可以查看/proc/xxx/maps,其中xxx 是您的进程的PID。

【讨论】:

  • 在 Gnome 中,系统监视器将显示内存映射。应用程序/系统工具/系统监视器→进程选项卡→右键单击进程→内存映射。窗口打开时经常显示为空,因为滚动条的位置很尴尬,所以如果你认为你有一个空白显示,请检查它们:-)
【解决方案3】:

虽然您提出了一个有效的问题,但我认为这不是您需要担心的事情。首先,100M 的二进制文件并非不可能。其次,系统加载器会将它需要的页面从 ELF(可执行和可链接格式)加载到内存中,并在必要时执行各种重定位等使其工作。它还将以相同的方式加载其所有必需的共享库依赖项。但是,这并不是一个非常耗时的过程,也不需要优化。可以说,任何“优化”都会产生很大的开销,以确保它不会尝试使用尚未在适当时候加载的东西,并且可能不太效率。

如果您对映射的内容感到好奇,正如 fge 所说,您可以查看 /proc/pid/maps。如果您想查看程序如何加载,可以尝试使用strace 运行程序,例如:

strace ls

它非常冗长,但它应该让您对 mmap() 调用等有所了解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-13
    相关资源
    最近更新 更多