发布时间:2026/8/31 2:07:39
Linux更新报text file busy/ETXTBSY?服务进程占用与动态库替换解析 更新 Linux 时还能继续使用这个现象很多人看到的第一反应是“这很正常”但等你在生产环境里跑一次apt upgrade或者yum update更新后业务进程起不来、动态库符号找不到、服务状态全是 failed你就知道那个“正常”其实是一个被隐藏起来的 bug。这篇文章要拆的就是为什么包管理器替换正在运行的二进制文件时会报text file busy/ETXTBSY以及怎么在更新前、更新中、更新后把这个问题彻底收住。如果你负责 Linux 服务器运维或者经常在本地 Linux 环境里跑 nginx、Python 脚本、数据库、AI 推理服务建议直接收藏。下面先给结论再看复现和修复步骤。1. 核心现象速览先把这个 bug 的关键信息列出来后面所有操作都围绕这张表展开。项目说明触发场景apt / yum / dnf 更新正在运行的二进制文件或动态库典型报错text file busy、ETXTBSY、ld.so符号找不到受影响对象systemd 服务、nohup 进程、supervisor 管理的驻留进程判断方式lsof、fuser、/proc/PID/exe、/proc/PID/maps直接修复方法更新模块前先停止服务更新后启动服务自动修复机制needrestart 或 checkrestart 自动检测并重启受影响服务适用发行版Debian/Ubuntu 系列、RHEL/CentOS/Fedora 系列适合读者Linux 运维、后端开发、自建服务维护者是否支持批量支持按服务列表或主机列表批量处理风险等级中高处理不好会导致服务短时不可用或无法启动这里的核心矛盾很简单Linux 文件系统允许你覆盖磁盘上的文件但如果你覆盖的是一个正在被进程执行的 ELF 二进制内核会拒绝写入直接抛ETXTBSY。这本来是保护机制但在包管理器的批量更新场景里它会让更新流程卡住或者让服务“边更新边运行”进入不一致状态。2. 适用场景与操作边界先说清楚这个 bug 会在哪类机器上出现。最容易踩中的场景服务器上有常驻进程比如nginx、python main.py、java -jar app.jar、postgres、redis。你执行了系统全量更新包管理器试图替换nginx二进制或底层动态库。服务没有配置systemd的ExecStop优雅退出或者包管理器的 maintainer 脚本没有正确处理新旧版本切换。最容易踩的坑更新过程显示成功但服务使用的还是旧的 inode 和旧的代码页。更新后直接重启服务服务加载了新的动态库但主程序还是旧的出现符号缺失。内核模块更新后没有重新加载系统日志里出现kernel: watchdog: BUG: soft lockup之类的软锁警告。不适合这种处理方式的场景内核或显卡驱动更新这类更新通常要求完整重启。数据库大版本升级必须走官方迁移流程。容器镜像构建过程的宿主机更新尽量避免边跑构建边更新。合规边界也必须说明如果你在用这个方案管理公司生产环境先确认服务所有者、更新窗口、回滚方案不要在生产上直接边更新边跑关键交易链路。3. 从报错入手定位正在占用文件的进程假设你在 Debian/Ubuntu 上执行sudo apt upgrade日志里出现这样的内容Preparing to unpack .../nginx_1.22.1-1_amd64.deb ... Unpacking nginx (1.22.1-1) over (1.22.0-1) ... dpkg: error processing archive .../nginx_1.22.1-1_amd64.deb (--unpack): trying to overwrite /usr/sbin/nginx, which is also in package nginx-core 1.22.0-1 dpkg-deb: error: paste subprocess was killed by signal (Text file busy)关键信息是Text file busy简称ETXTBSY。它表示/usr/sbin/nginx正被某个进程当作可执行文件映射到了地址空间。内核不允许更新工具直接覆盖这个文件。先确认是哪个进程占用了它。# 查看所有进程中对 /usr/sbin/nginx 的引用 lsof /usr/sbin/nginx # 更精确一点只列出进程 PID 和命令名 lsof -t /usr/sbin/nginx如果 lsof 没装用 fuserfuser -v /usr/sbin/nginx输出示例USER PID ACCESS COMMAND /usr/sbin/nginx: root 1234 ...e nginx也可以直接看/proc目录确认进程的真实可执行文件指向ls -l /proc/1234/exe cat /proc/1234/maps | grep nginx | head -5当你看到/proc/1234/exe指向的文件路径是/usr/sbin/nginx (deleted)时说明磁盘文件已经被替换过但进程还在使用旧文件。这就是典型的“更新时还能继续使用”状态。动态库占用的情况类似但不像 nginx 主程序那么明显。例如 Python 服务占用了libpython3.11.so.1.0此时执行apt upgrade也容易报错或产生状态漂移# 查看某个动态库被哪些进程引用 lsof /usr/lib/x86_64-linux-gnu/libpython3.11.so.1.04. 复现实验最小环境还原 bug为了说清楚“更新时还能继续使用”为什么是个 bug可以在本地 Linux 环境做一个最小复现。首先写一个简单的 Python 驻留脚本# /tmp/demo_server.py import time print(server started, pid , time.time()) counter 0 while True: counter 1 time.sleep(5)后台启动它nohup python3 /tmp/demo_server.py /tmp/demo.log 21 echo $!此时用进程 PID 确认可执行文件cat /proc/PID/exe ls -l /proc/PID/exe然后尝试覆盖正在运行的脚本文件# 直接覆盖原脚本 echo print(hacked) /tmp/demo_server.pyPython 脚本是纯文本解释执行覆盖通常不会报ETXTBSY但进程实际读取的是磁盘上的新内容这已经是一个隐蔽问题。真正的ETXTBSY更容易出现在可执行二进制上。下面用一个小型 C 程序来演示// /tmp/demo_bin.c #include stdio.h #include unistd.h int main() { printf(demo binary running, pid %d\n, getpid()); sleep(60); return 0; }编译并运行gcc /tmp/demo_bin.c -o /tmp/demo_bin /tmp/demo_bin sleep 1 # 尝试覆盖正在运行的二进制 cp /tmp/demo_bin /tmp/demo_bin.bak echo int main(){return 1;} /tmp/evil.c gcc /tmp/evil.c -o /tmp/evil cp /tmp/evil /tmp/demo_bin第二次cp会提示cp: cannot create regular file /tmp/demo_bin: Text file busy这就是整个 bug 的最底层原理。生产环境的apt更新失败日志本质上是同一个问题只是包管理器会尝试卸载旧的、安装新的中途如果 maintainer 脚本没有停止服务就会被卡住或者留下不完整状态。复现完毕后清理测试进程和文件pkill demo_bin pkill -f demo_server.py rm -f /tmp/demo_bin /tmp/evil /tmp/demo_server.py5. 修复方案从手动到自动5.1 方案一更新前手动停止服务这是最直接的方式适合单机、服务数量不多的情况。# 停止受影响的服务 sudo systemctl stop nginx # 执行更新 sudo apt upgrade # 启动服务 sudo systemctl start nginx停止服务会释放二进制文件和动态库的占用包管理器可以正常覆盖。启动服务时加载的新文件版本和磁盘上的动态库一致不会出现符号缺失。5.2 方案二使用 needrestart 自动检测Debian/Ubuntu 环境强烈建议安装并配置 needrestart。sudo apt install needrestartneedrestart会在 apt 更新流程的末尾自动扫描正在运行的进程对比进程使用的二进制文件和动态库版本。如果发现有进程还在使用旧版本它会列出可重启的服务并给出重启建议。你可以把自动模式打开sudo sed -i s/#\$nrconf{restart} i;/\$nrconf{restart} a;/ /etc/needrestart/needrestart.conf这里的restart a表示自动重启受影响服务。如果你不想自动重启可以保持交互模式每次更新时手动确认。RHEL/CentOS/Fedora 系列可以使用checkrestart或者dnf check-restartdnf check-restart安装方式sudo dnf install yum-utils sudo check-restart5.3 方案三systemd 单元自动优雅退出如果你自己维护服务可以在 systemd 单元文件里增加 update 前的钩子。例如/etc/systemd/system/nginx.service.d/override.conf[Service] ExecStopPost/usr/bin/systemctl stop nginx ExecStartPre/usr/bin/systemctl start nginx但这只在服务管理启停时有效apt upgrade的维护脚本未必会执行这个自定义逻辑。更可靠的方式是在 systemd 单元中定义RefuseManualStop或使用BindsTo依赖关系让包管理器在更新前通过服务管理接口停机。实际运维中我建议优先走方案二。needrestart 已经覆盖了常见动态库和二进制文件比自己写钩子可靠。5.4 方案四更新后核对并修复如果更新已经跑完进程还在用旧文件可以先检查坏掉的服务systemctl --failed确认服务失败原因journalctl -u my-service --no-pager -n 50动态库版本不匹配时运行程序可能会报error while loading shared libraries: libssl.so.3: cannot open shared object file修复方式# 刷新动态库缓存 sudo ldconfig # 检查动态库链接情况 ldd /usr/bin/your-command如果ldd显示某个库指向了not found先用dpkg -S或rpm -qf找到这个库属于哪个软件包再确认是否已安装最新版本。dpkg -S libssl.so.3 rpm -qf /usr/lib64/libssl.so.36. 自动化更新与批量环境处理6.1 更新前资源占用检查脚本在更新大量服务之前先写一个脚本检查哪些进程占用关键动态库和二进制这能提前预判报错。#!/bin/bash # check_updated_processes.sh # 用法bash check_updated_processes.sh echo 检查 /usr/sbin/nginx 占用进程 lsof /usr/sbin/nginx 2/dev/null || echo 没有进程占用 echo 检查 /usr/lib/x86_64-linux-gnu/libssl.so.3 占用进程 lsof /usr/lib/x86_64-linux-gnu/libssl.so.3 2/dev/null || echo 没有进程占用 echo 检查所有 systemd failed 状态 systemctl --failed --no-pager更新命令前先执行一次能提前知道哪些服务会被影响。6.2 批量环境中串行更新在有多台服务器的环境里不建议使用批量并发更新因为并发更新可能会同时把多台机器的服务打挂。下面是一个简单的批量脚本按主机列表串行执行并在每台机器更新后等待健康检查结果#!/bin/bash # batch_update.sh HOSTS(192.168.1.10 192.168.1.11 192.168.1.12) USERNAMEdeploy for host in ${HOSTS[]}; do echo 开始更新 $host ssh $USERNAME$host \ sudo apt update sudo DEBIAN_FRONTENDnoninteractive apt upgrade -y sudo needrestart -r a echo $host 更新结束 done这里的needrestart -r a表示自动重启受影响服务。批量环境下如果使用了 Ansible可以改用ansible-playbook的serial: 1来控制更新节奏。6.3 批量任务失败重试批量更新最大的风险是中间某台机器失败。建议在脚本中加入重试逻辑# 失败自动重试 3 次 for retry in 1 2 3; do sudo apt upgrade -y break echo 第 $retry 次更新失败5秒后重试 sleep 5 done如果重试仍然失败记录日志并跳过不要卡死整个队列。7. 资源占用与性能观察7.1 更新过程中需要盯的系统指标更新期间建议并行观察几个指标# CPU 和平均负载 uptime top # 磁盘 IO iostat -x 1 5 # 内存占用 free -h # 正在进行的事务 ps aux --sort-%cpu | head -207.2 needrestart 的资源开销needrestart 会在更新后扫描进程和动态库。这个扫描过程在服务数量较少的机器上几乎不消耗资源但如果是大型服务器进程数上千可能在更新结束时带来短暂的 CPU 峰值。建议在业务低峰期执行更新。7.3 如何判断系统已经不稳定如果更新过程中出现下面几个信号说明系统正在进入不稳定状态平均负载持续升高即使没有业务请求。systemctl --failed出现新的 failed 单元。dmesg中出现大量 glibc、libssl 相关的动态链接错误。进程频繁崩溃日志里出现Cannot allocate memory或SIGSEGV。这时候如果还在继续使用系统不要继续安装新软件包先处理已经损坏的服务。8. 常见问题与排查方法下面这张表整理了我遇到这个问题后最常走的排查路径。问题现象可能原因排查方式解决方案apt 更新报 Text file busy正在运行的二进制被包管理器覆盖lsof path找到占用进程更新前systemctl stop对应服务更新后服务启动失败报动态库找不到新二进制加载旧库或库路径混乱ldd /usr/bin/xxx、ldconfig -p执行ldconfig确认库包已安装服务进程还在但功能异常进程使用了旧 inode 的代码ls -l /proc/PID/exe看是否有 deleted重启服务UPGRADE 日志正常但系统重启后内核模块加载失败内核驱动与当前内核版本不匹配dmesggrep -i moduledpkg 更新卡住重复执行 apt 不生效中断的 dpkg 锁或未完成配置ps auxgrep dpkgyum/dnf 更新报 rpmdb 损坏强制中断更新导致 rpm 数据库不一致rpm --rebuilddb备份数据库后重建 rpmdb批量更新时多台机器同时失败并发更新互相争抢资源或同一时间拉取仓库检查每台机器单独日志改为串行更新限制并发数9. 最佳实践与工程化建议9.1 第一次部署先小范围验证不要一上来就在生产全量更新。先找一台测试机执行完整更新流程记录下面的内容更新前dpkg -l或rpm -qa的软件包快照。服务的启动命令和启动参数。更新前后systemctl status的变化。9.2 保留最小可运行环境准备一个最小依赖环境方便在更新失败时快速启动服务。这个环境可以是 Docker 容器也可以是独立的 Python venv原则是尽量不要让宿主机上的一次全量更新破坏你最重要的服务。9.3 模型文件、输入素材、日志分目录管理如果你在 Linux 上运行 AI 推理服务比如本地部署的 OCR、TTS 或大规模语言模型更新系统或 Python 依赖前要把模型文件单独放目录日志单独放目录。更新时尽量只改系统软件包不要动不动升级整个 PyTorch 或 CUDA 版本。CUDA 更新很容易碰到显卡驱动不匹配的问题更新前先确认当前驱动版本再决定是否需要更新。9.4 日志和失败重试机制批量更新环境必须有日志。把每台机器的更新输出重定向到独立日志文件LOG_DIR/var/log/update_records mkdir -p $LOG_DIR LOG_FILE$LOG_DIR/$(date %Y%m%d%H%M%S)_$(hostname).log sudo apt upgrade -y 21 | tee $LOG_FILE9.5 接口服务要限制访问范围如果你在服务器上开放了 WebUI 或 API 服务更新期间建议做好访问控制。不要因为更新顺便把所有端口全部暴露容易扩大安全风险。9.6 涉及人脸、声音、版权素材时的边界如果这台 Linux 服务器跑的是数字人、声音克隆、图像生成或视频合成工具更新前后的素材使用必须确认授权。任何人脸、声音、版权图像的处理都要有授权依据不要因为工具本身可用就随意处理他人素材。9.7 发布或商用前的效果复核更新完系统后如果服务涉及图像识别、语音合成等 AI 任务一定要重新跑一遍标准测试用例。验证输出质量和更新前保持一致。如果输出退化优先检查模型权重文件是否被动到再检查推理框架版本是否被降级或升级。10. 总结与下一步这次排查的起点是 update 日志里的Text file busy但真正的问题不在报错本身而在“更新时系统还能继续使用”这张假象造成的信任感上。进程还能跑、页面还能打开、服务状态还是 active这些都不等于状态一致。只要磁盘上的二进制或动态库已经被替换进程实际上已经持有旧版本的文件映射下次重启随时会翻车。我建议你先做三件事在测试环境复现一次这个报错确认lsof和fuser的输出格式。给生产环境安装 needrestart并把自动重启参数调成a先在一台低负载机器上验证。写一个更新前脚本检查关键服务的二进制占用情况。如果你平时还要管理多台机器可以进一步把更新流程和systemd --failed健康检查串起来做成无人值守的批量更新任务。后续如果发现更新后某个服务虽然重启成功但行为异常优先看看 services 的ExecStartPre和ExecStopPost钩子有没有被包管理器覆盖再检查动态库版本。这个 bug 不复杂但它能在凌晨 2 点把一次本来只需要 5 分钟的例行更新拖成 2 小时故障演练。提前把它处理好省下的时间都算自己的。

相关新闻

2026/8/31 2:07:38

Barret Zoph加盟Google背后:强化学习主导语言模型后训练

Barret Zoph 离开 OpenAI、加入 Google 出任研究副总裁,这条消息在 AI 社区很快引发了讨论。多数讨论集中在人才流动、公司竞争和薪酬待遇上,但技术从业者可以从中读到的,其实是一个更明确的研究方向信号:强化学习在语言模型后训练…

2026/8/31 2:02:38

Codex AI编程工具安全实践:从CLI路径到权限边界

Codex 这类终端里的 AI 编程工具,正在从“能跑通”变成很多人日常开发的一部分。它直接读取你的仓库、帮你执行命令、生成补丁甚至提交代码,所以个人使用时的安全实践,本质上不是“要不要用”的问题,而是怎么把权限边界、敏感信息…

2026/8/31 2:02:38

MS41929步进电机驱动demo剖析:C/C++混编与ms932编码避坑指南

简介:本资源是一套基于STM32F103与MS41929双通道步进电机驱动芯片的嵌入式控制DEMO工程,面向嵌入式开发初学者、电机控制实践者及自动化项目开发者,解决双步进电机同步驱动、细分模式切换与实时运动轨迹控制等典型工程问题。压缩包共198个文件…

2026/8/31 2:22:40

M项目升级到v26.1.2的完整方法论:确认、升级、验证与回滚

“听说 M 更新到了 26.1.2”,这句话在开发者群里出现的时候,千万别急着去升级。版本号背后有可能是功能增强,也有可能是破坏性变更,还有可能是某个第三方分支的“伪更新”。这篇文章就以“M 项目更新到 v26.1.2”为线索&#xff0…

2026/8/31 2:22:40

大模型游戏表现测评实战:从任务设计到结果解读的完整方法

大模型游戏表现测评,最近被 Epoch AI 实测 GPT-5.6 游戏表现这类话题带热了。很多人第一反应是看分数、看排名,但我更建议先琢磨一个问题:这个分数是在什么任务、什么环境、什么判断标准下得到的。因为大模型玩游戏和人类玩游戏完全不是一回事…

2026/8/31 2:22:40

MiniMax H3基础模型本地部署与后训练实战指南

MiniMax 开源 H3 基础模型后,社区里讨论最密集的不是模型效果本身,而是两件事:怎么在本地把它跑起来,以及怎么在它的权重上做后训练。原因不难理解,基础模型通常指完成了大规模预训练、但还没有经过完整指令对齐的通用…

2026/8/31 2:22:40

Grok模型微调实战:从环境准备到批量处理全流程

Grok 模型的训练与微调,最近讨论热度一直不低。我自己做本地实验时发现,真正卡住多数人的往往不是模型能力,而是从环境准备、数据整理到参数调整这条链路没理顺。这篇文章按一次完整实测的顺序来写,把 Grok 相关模型的运行、微调和…

2026/8/31 2:22:40

基于51单片机的智能垃圾桶设计:定时器、PWM与传感器实战

简介:本资源是一套基于51单片机的智能垃圾桶嵌入式系统完整开发方案,面向电子类专业初学者、课程设计学生及单片机入门实践者,解决自动感应开盖、垃圾量检测与满溢提醒等典型物联网终端控制问题。压缩包共24个文件,含Keil工程核心…

2026/8/31 2:17:39

Jetpack Compose 约束布局实战:ConstraintLayout 核心 API 与调试

先给结论:Jetpack Compose 里的 ConstraintLayout 是一套声明式 UI 约束布局方案,适合处理复杂嵌套、相对定位和比例对齐的场景。很多人问它和传统 View 体系里的 ConstraintLayout 有什么区别,也有人纠结到底什么时候才值得用。这篇文章把依…

2026/8/31 1:05:20

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/8/31 2:14:20

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/8/31 1:41:28

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/8/31 0:07:32

STM32C5设备支持包(IAR DFP)安装指南与常见坑

上一阵子在IAR里折腾一块基于STM32C5系列的新板子,工程从STM32CubeMX导出来之后怎么都编译不过。报错信息很干脆:找不到设备描述文件。跟着错误路径去查,发现指向的是一个让我愣了一下的名字:STMicroelectronics.stm32c5xx.2.1.0.…

2026/8/31 0:07:32

STM32N657 SWO引脚矛盾:CubeMX显示PB3,数据手册为PB5

拿到STM32N657这颗料的第一天,我就撞上了一个让人原地懵圈的引脚矛盾:CubeMX里清清楚楚显示SWO在PB3,翻开数据手册的引脚说明表,却赫然写着PB5。对于一个靠SWO输出调试日志吃饭的人而言,这种"工具和手册打架"…

2026/8/28 16:16:48

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/28 16:16:50

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/28 11:06:45

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…