Linux 用 cp 覆盖运行中的程序时报 Text file busy
Linux 用 cp 覆盖运行中的程序报 Text file busy 时,检查实际执行映像;安排正常停机安装,或按同挂载点原子替换流程发布,再核对运行版本。
Linux 上升级一个正在运行的原生程序,sudo cp 覆盖现有可执行文件时提示 Text file busy,而删除旧路径后再复制似乎能成功。这里应检查目标是否仍被当作执行映像使用:Linux 会拒绝对正在执行的映像请求写访问,root 身份也不会让这种原地覆盖变成允许的操作。
这篇针对内核直接执行的 ELF 等原生程序。脚本由解释器读取、动态库被映射,以及网络文件系统的规则都有各自的边界,不能把所有“文件正在使用”都按同一办法处理。
确认报错指向正在执行的那个文件
典型示例为:
cp: cannot create regular file '/opt/example-app/bin/worker': Text file busy
先核对路径、文件类型和服务实际运行的程序。以下是只读检查示例,example-worker.service 是示例服务名:
file /opt/example-app/bin/worker
systemctl show example-worker.service -p MainPID -p ExecStart
如果实际进程 PID 为 1234,还可以在有权限的环境中查看:
readlink /proc/1234/exe
PID 应来自当前运行状态,不能照抄示例数字。/proc/PID/exe 指向实际执行的映像,读它也受权限限制;看不到链接不证明程序没有运行。服务主进程、子进程和其他实例可能使用同一程序,应结合进程管理方式核对。
磁盘路径已被替换后,旧进程的链接可能带有 (deleted)。这说明它仍关联原来的映像,不能据此认定进程已经升级到磁盘上的新文件。
可安排停机时,先正常结束旧实例
按照应用支持的停止流程等待工作结束,再确认所有使用目标映像的实例都已退出,随后安装经过验证的新版本并启动。服务停止会影响请求或任务,需要维护窗口、队列处理和数据保护措施;不要看到一个 PID 就直接强杀,也不要为覆盖单个程序重启整台机器。
复制前留存可恢复的旧版本和其元数据,核对目标目录、体系结构、动态依赖、属主、权限,以及应用所需的扩展属性或文件能力。新文件能复制成功,不证明它在当前环境能正常启动。
如果所有实例都已退出仍报同样的错误,应重新核对实际路径、自动拉起的进程和存储环境。Text file busy 还有其他内核使用情形;不要把它与普通权限不足混在一起处理,也不要反复给目标增加写权限。
保留旧进程运行时,用新文件替换路径
原地写旧文件和替换目录里的名字不同。将新程序完整写到另一个文件,再在同一挂载文件系统内通过 rename 替换目标路径,会让后续启动使用新文件;已运行的进程仍使用旧映像,不会因为名字相同而自动换成新代码。
这种发布方式应交给应用既有的安装或发布流程。若需要自己实现,暂存文件必须放在可信、权限受控的目录中,完整写入并核对内容和所需元数据后才切换。Linux rename 对已存在的目标提供原子路径替换;跨挂载点不能直接套用这项保证,某些 mv 操作会改走复制和删除。
不要把“先 rm,再 cp”作为无缝发布:删除到复制完成之间,路径可能不存在,或新文件尚未写完整,新启动的进程会失败。也不要使用固定且可能已存在的暂存名字,误覆盖另一轮发布。
原子替换的是这个路径的文件条目,不是整个应用的运行状态。它不会同步升级配置、动态库、数据库结构或全部硬链接,也不承诺断电后的持久性。发布仍需按应用规则完成版本切换,控制新旧实例兼容和必要的重启。
文件更新与运行版本分别验收
切换后先核对目标文件的校验值、版本和元数据,再确认真正接管任务的新进程已启动正确版本,原有实例按计划结束。只比较当前磁盘文件名或只看到 cp 返回成功,都不足以确认升级完成。
若新版本无法启动,保留失败信息,在可控的服务状态下恢复备份的旧文件及元数据,再按旧版本启动规则验证。恢复磁盘文件不会自动将已经运行的新进程换回旧程序;若发布已改变业务数据或配置,也需使用各自的恢复方案。
这条路径不应直接扩展到正在读取的脚本或动态库。脚本能否看到中途变化取决于解释器的读取方式,动态库的原地修改也可能破坏正在使用它的进程;这些对象应沿应用支持的完整发布方式处理。


