标准答案
- 父进程常先 fork,再由子进程调用 execve 用新程序替换当前地址空间;execve 成功后不会返回。
- 内核解析 ELF,建立代码段、数据段、栈和堆等映射;动态链接程序还会加载解释器和依赖的共享库。
- 运行时完成重定位、全局对象或模块初始化,然后调用应用入口。
- 排查启动失败时应区分文件不存在、没有执行权限、解释器或共享库缺失、参数环境错误以及程序启动后主动退出。
题目解析
用户态程序通常由 shell 或父进程调用 execve,内核读取 ELF 头,建立代码、数据、堆和栈映射,再由动态链接器加载共享库,最后跳到程序入口和运行时初始化。fork 主要复制进程的执行上下文并使用写时复制,真正替换成另一个程序通常由后续 exec 完成。
启动失败要区分“无法找到文件”“权限拒绝”和“运行后立即退出”。除了目标文件的执行位,还要检查每一级父目录的搜索权限、shebang 解释器是否存在、文件系统是否 noexec、架构是否匹配,以及动态库是否缺失。strace、file、ldd 和 systemd journal 能把这些阶段区分开。
常见误区
- 误区:认为 fork 会直接把新程序加载完成。改正:区分 fork 创建进程和 exec 加载新程序两个阶段。
- 误区:只看目标文件的 x 权限。改正:同时检查父目录搜索权限、shebang、解释器、挂载选项、架构和动态库。