当二进制文件运行时,它是否会立即将其整个二进制数据复制到内存中?我可以改变吗?

时间:2011-12-14 15:14:22

标签: c linux binary

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

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

3 个答案:

答案 0 :(得分:32)

应用程序级程序员的理论模型看起来就是这样。事实上,正常的启动过程(至少在Linux 1.x中,我相信2.x和3.x是优化但相似)是:

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

但是,一般情况下,直到你的进程大于可用内存 - 并且数据几乎总是比可执行文件大得多 - 你可以安全地假装你在这个世界中独处,并且没有发生这种请求分页的事情。 / p>

所以:实际上,内核已经 在加载程序时运行你的程序(如果你从未加载过某些页面,可能永远不会加载那些代码/引用那些数据)。

如果您的创业公司特别迟钝,您可以查看prelink系统以优化共享库负载。这减少了ld.so在启动时(在程序的execmain被调用之间以及首次调用库例程时)必须执行的工作量。

有时,静态链接可以提高程序的性能,但是占用RAM的主要成本 - 因为您的库不是共享的,除了共享{{1}之外,您还复制了“libc”例如,每个其他程序正在使用的。这通常仅适用于程序在机器上运行或多或少运行的嵌入式系统。

(*)实际上,内核有点聪明,通常会预加载一些页面 减少页面错误的数量,但理论是相同的,无论如何 优化

答案 1 :(得分:6)

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

我不知道哪种工具可以实时显示,但您可以查看/proc/xxx/maps,其中xxx是您的流程的PID。

答案 2 :(得分:2)

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

如果你很好奇被映射的内容,正如fge所说,你可以检查/ proc / pid / maps。如果您想了解程序的加载方式,可以尝试使用strace运行程序,例如:

strace ls

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