Linux上有个二进制程序一直在运行,修改代码后重新编译把原来的二进制程序覆盖了,会怎么样? 该问题来自一道天美后台开发面试题:天美一面 后台开发(凉) - 牛客面经的文章 - 知乎。此处尝试进行回答。


第一想法一般是:“原程序被操作系统加载进内存,不会受到影响。”
系统会创建一个新的inode指向新文件,而正在运行的进程仍会继续使用旧的inode对应的代码段。

但实际上拓展到一个问题:“二进制文件会全部加载到内存吗?”

ELF二进制文件在加载时,操作系统通常采用按需分页的机制,只将当前需要的部分加载到物理内存,而不是一次性加载整个文件。虚拟内存映射允许文件的部分内容驻留在磁盘,直到被访问时才调入内存。
同时,动态链接库的延迟加载和内存映射文件技术(mmap)也帮助减少实际内存占用。因此,如果二进制文件很大,不会全部加载到物理内存中,而是按需加载,利用虚拟内存管理技术优化资源使用。

但是:真的会在运行时加载新的内容吗?

ELF文件在启动时如何决定哪些内容加载到内存?主要依赖于其程序头表(Program Header Table)​中定义的段(Segment)信息。程序头表由多个Elf64_Phdr结构体组成,每个结构体描述了一个需要加载到内存的段(如代码段、数据段、动态链接信息段等)。这些段通常包含多个(Section)的集合。

覆盖原文件后,旧文件的磁盘空间不会被立即释放,需等待所有关联进程结束后才能回收(通过lsof可查看占用进程)。

总结
​已运行的进程不受影响:Linux通过inode标识文件,旧进程继续执行内存中已加载的旧代码,与原磁盘文件解耦。


1. ELF文件的按需加载机制

ELF二进制文件通过程序头表(Program Header Table)中的PT_LOAD段描述需要加载的代码和数据区域。内核的load_elf_binary()函数会将这些段映射到进程的虚拟地址空间,但实际物理内存的占用是按需分页的: • 仅当程序访问某个页(通常4KB大小)时,才会触发缺页异常,将对应内容从磁盘加载到物理内存。 • 未使用的代码或数据(如未执行的函数)可能永远不会被加载到物理内存中。

2. 虚拟内存映射与内存优化

虚拟内存优势:ELF文件通过mmap()系统调用映射到虚拟地址空间,此时文件内容并不直接占用物理内存,而是由内核通过页表管理。 • 写时复制(Copy-on-Write):对于只读段(如代码段),多个进程可以共享同一物理内存页;对于可写段,修改时才会复制新页。

3. 动态链接与延迟加载

动态链接库(如.so文件)在程序运行时通过ld-linux动态加载器按需载入。例如: • 首次调用某个库函数时,动态链接器才会加载对应的代码段到内存。 • 部分库可能仅在特定条件下被使用,从而减少初始内存占用。

4. 大文件的实际内存占用

物理内存限制:若二进制文件极大(如8GB),但程序实际执行的代码路径有限,物理内存占用可能远小于文件大小。 • 交换空间(Swap):当物理内存不足时,操作系统会将不活跃的内存页交换到磁盘,腾出空间供当前进程使用。