它执行之前是否将整个二进制文件复制到内存? 我对这个问题很感兴趣,想把它改成其他的方式。 我的意思是,如果二进制是100M大(似乎不可能),我可以运行它,而我把它复制到内存中。 这可能吗?
或者你能告诉我如何看待它的运行方式? 我需要哪些工具?
应用程序级编程人员的理论模型使其看起来如此。 事实上,正常的启动过程(至少在Linux 1.x中,我相信2.x和3.x是经过优化但类似的)是:
ld.so
程序(例如/lib/ld-linux.so.2
)为共享库设置了内存映射 jmp
(对于C程序来说,就像crtprec80
,它调用main
)。 由于它只设置了映射,而没有实际加载任何页面(*),所以会引起CPU的内存管理单元的页面错误,这是内核的一个中断(异常,信号)。 malloc
/ new
,内核创建RAM的读写页面(没有光盘备份文件),并将它们添加到你的虚拟地址空间。 malloc
“创建”的malloc
),它们会被写入(page file = swap file = swap partition = on-disc virtual memory)。 访问这些“释放”的页面会导致另一个页面错误,并重新加载。 但是,一般来说,直到你的过程比可用的RAM更大 – 而且数据几乎总是比可执行文件大得多 – 你可以安全地假装你独自一人在这个世界上,没有任何这种需求分页的事情正在发生。
所以:实际上,内核已经在加载程序的时候运行你的程序了(如果你永远不会跳进那个代码/引用那个数据,甚至可能永远不会加载一些页面)。
如果你的启动特别慢,你可以看看prelink
系统来优化共享库加载。 这减少了ld.so
在启动时(在你的程序的exec
和main
调用之间,以及你第一次调用库函数之间)所要做的工作量。
有时候,静态链接可以提高程序的性能,但是由于RAM的主要开销 – 因为你的库不共享,所以除了每个其他程序正在使用的共享libc
以外,还要复制“ libc
” 。 这通常只在你的程序在机器上运行得多或少的嵌入式系统中有用。
(*)事实上,内核有点儿聪明,通常会预装一些页面来减少页面错误的数量,但是理论是一样的,不管优化
不,它只将必要的页面加载到内存中。 这是按需分页。
我不知道一个能够实时显示的工具,但你可以看看/proc/xxx/maps
,其中xxx
是你的进程的PID。
当你问一个有效的问题时,我不认为这是你需要担心的事情。 首先,100M的二进制不是不可能的。 其次,系统加载器会将它需要的页面从ELF(Executable and Linkable Format,可执行和可链接的格式)加载到内存中,并执行各种重定位等,如果需要的话,将使其工作。 它也将以相同的方式加载所有必需的共享库依赖项。 但是,这不是一个非常耗时的过程,并且不需要进行优化。 可以说,任何“优化”都会有一个很大的开销,以确保它不会尝试使用那些在适当时候没有被加载的东西,而且可能效率会更低 。
如果你很好奇映射的东西,就像fge说的那样,你可以检查/ proc / pid / maps。 如果你想看看一个程序如何加载,你可以尝试用strace
来运行一个程序,例如:
strace ls
这是相当详细的,但它应该给你一些mmap()调用等的想法。