Watch at Ease看见世界,读懂生活。

科技 · 实用指南

Linux 提示“Text file busy”?覆盖或重试前先锁定正在执行的 inode

ETXTBSY 可能表示部署正在写入运行中的可执行文件,也可能表示启动器要执行一个仍以写方式打开的文件。终止任何进程前先保留失败系统调用、进程、inode 与命名空间,再修复竞态或原子替换路径。

运行中 inode 与受阻就地覆盖示意图
原创插画:修改路径前先证明运行中的 inode。 Watch at Ease

ETXTBSY 可能表示部署正在写入运行中的可执行文件,也可能表示启动器要执行一个仍以写方式打开的文件。终止任何进程前先保留失败系统调用、进程、inode 与命名空间,再修复竞态或原子替换路径。

可执行文件仍以写方式打开时,execve() 可能返回 ETXTBSY。 同一 inode 正在执行时,写入或截断可能被拒绝。

1. 保留方向、路径与 inode

记录完整错误、时间、命令、PID、容器或挂载命名空间,以及真正失败的操作。区分 exec、open、truncate、copy 与 rename;同一句报错不代表同一方向。重启前用 stat 保存设备号和 inode。

不要先重启、pkill -f 或无限重试。替换后同一路径可能指向另一个 inode,容器也可能看不到主机上的占用进程。必须在失败命名空间中比较设备号与 inode,而不是只比较路径文字。

2. 在正确命名空间证明执行者或写入者

怀疑运行中可执行文件时,检查 /proc/<pid>/exe,并取得小范围的 fuser -v 或等价进程证据。可执行标记与写入标记回答不同问题;注意权限,不要把无关命令行或路径公开。

执行失败时,找出仍持有可写描述符的进程,核对描述符目标与标志。还要检查父子进程,因为未设置 close-on-exec 的写描述符可能被并发启动的子进程继承。只停止已证实的所有者。

3. 修复写后执行竞态,而不是掩盖它

复现最小序列:创建同目录新文件,完整写入,设置权限,关闭后再执行。并发负载下串行化缓存解压,或使用按版本隔离的临时路径,避免两个 worker 同时填充和执行同一目标。

从源头缩短描述符生命周期,并在合适处设置 close-on-exec。只针对 ETXTBSY 的短暂有界重试可以缓解残余竞态,但必须可观测,不能替代关闭写入者或消除共享路径竞争。

4. 写入同目录新文件并原子替换路径

不要就地截断运行中的可执行文件。在同一文件系统写入新文件,设置所有者和模式,校验哈希并直接测试,然后用 rename 覆盖目标。原子路径切换后,旧进程仍通过旧 inode 运行。

如果还要求崩溃持久性,应按应用需求同步文件与目录。重命名后只重启或重载受控服务,并保留旧制品供回滚,不要立刻删除证据。

5. 用进程和 inode 证据验收恢复

确认新 PID 的 /proc/<pid>/exe 指向预期路径、设备号、inode 与内容哈希。若旧 PID 被有意保留,确认它在排空前仍映射旧 inode。

运行过去能触发 ETXTBSY 的并发写入与启动测试。验收要求无 busy 错误、可执行文件上无残留可写描述符、权限和哈希正确且回滚已测试;任一门槛失败就恢复旧路径并继续调查。

已验证新文件通过原子重命名切换的示意图
原创插画:验证同目录新文件,原子重命名后排空旧 inode。 Watch at Ease