SSH远程安装deb包遇Broken pipe错误?从管道原理到磁盘排查的完整手册
2026/9/16 1:19:35 网站建设 项目流程

还真被我遇上了。手头一台麒麟系统的服务器,远程通过 SSH 安装一个软件升级包,本来敲完dpkg -i等着进度条走,结果终端啪地弹出来一行:

dpkg-deb: error: paste subprocess was killed by signal (Broken pipe)

后面还跟着一句 SSH 的packet write wait: connection to 192.168.1.11 port 22: broken pipe。那台机器不在手边,只能靠命令行操作,一时之间还真有点慌。这类报错看着简单,但“Broken pipe”这个词其实横跨了好几层:可能是 dpkg 的问题,可能是磁盘问题,也可能是 SSH 连接问题。当时折腾了半个多小时才彻底排除,后来把这类错误的排查思路和手法整理了一遍,顺手也把容易踩的坑标了出来。无论是麒麟系统还是其他基于 Debian 的发行版,只要用 dpkg 装 .deb 包,大概率都用得上。

1. 错误现场与故障现象还原

1.1 错误信息的完整特征

先还原一下我当时看到的完整终端输出,免得你遇到类似情况时对不上号:

$ sudo dpkg -i upgrade-network-xxx.amd64.deb (Reading database ... 32867 files and directories currently installed.) Preparing to unpack upgrade-network-xxx.amd64.deb ... Unpacking upgrade-network-xxx (1.0.2) over (1.0.1) ... dpkg-deb: error: paste subprocess was killed by signal (Broken pipe) dpkg: error processing archive upgrade-network-xxx.amd64.deb (--install): dpkg-deb --control subprocess returned error exit status 2 Errors were encountered while processing: upgrade-network-xxx.amd64.deb

注意这里的错误有两段:第一段是dpkg-deb: error: paste subprocess was killed by signal (Broken pipe),第二段是 dpkg 把错误抛给你,说--control subprocess returned error exit status 2。如果是在本地没有 SSH 的场景,基本上只会有前半段,后面跟的则是 SSH 客户端的 broken pipe 报错。

报错的关键词是paste subprocess。这里的paste不是让你粘帖东西,而是 dpkg-deb 内部调用的一个 Linux 文本处理命令。当 dpkg-deb 需要从 deb 包中提取控制信息或数据内容时,会先生成 tar 格式的数据流,再通过管道交给paste这类命令做行处理。一旦后面的步骤没有正常接收数据,前面的进程就会收到系统发来的 SIGPIPE 信号,然后被终止。最终表现就是“Broken pipe”。

1.2 发生场景的共性分析

我个人总结下来,出现这个错误主要有三类场景:

  • 本地终端安装 .deb 包:在图形界面或本地 console 上执行dpkg -i,突然报错,多半和系统资源、软件包完整性有关。
  • 通过 SSH 远程安装:这是我最常遇到的场景。远程终端会话本身不稳定,一旦连接断开,正在跑的 dpkg 进程也容易跟着遭殃。
  • 自动化脚本或 CI/CD 里执行 dpkg 操作:脚本中封装了dpkg-deb -xdpkg -i,如果脚本提前退出、管道被关闭,同样会触发。

无论是麒麟系统还是 Debian/Ubuntu,只要底层用的是 dpkg 体系,这个报错的排查逻辑都是相通的。麒麟系统作为基于 Debian 定制的发行版,日常安装本地软件包时用得尤其多。我们就围绕这个错误,从原理到底层再到实操,一层层拆开来看。

2. 根因剖析:为什么会出现 Broken pipe

2.1 管道机制与 dpkg-deb 的关系

“Broken pipe”字面意思是“管道破裂”。Linux 下管道是一种进程间通信方式,前一个命令的输出直接作为后一个命令的输入。管道本身是个缓冲区,数据从上游流向缓冲区,再由下游读取。如果下游进程已经退出,上游进程仍然往管道里写数据,系统就会向上游发送 SIGPIPE 信号,默认行为是终止这个进程。

dpkg-deb 在处理 .deb 包时,内部并不是一步到位解压文件的,而是有多级处理流程。它先把 deb 包里的control.tar.gzdata.tar.*解出来,再通过管道交给pastetar之类的命令去进一步拆包。如果中间的某个下游进程因故退出,上游就会因为管道没有读取端而收到 SIGPIPE,然后被杀掉。杀掉的信号会在错误信息里体现为(Broken pipe)

所以看到 dpkg-deb 报这个错,第一反应不应该仅仅是“软件包坏了”,而应该想到:管道对端为什么提前退出了?这才是问题的核心。

2.2 常见的几类触发原因

根据我的实践经验,导致“管道对端提前退出”的原因可以分为这么几类:

分类具体原因表现特征
系统资源不足磁盘空间满、inode 耗尽解压时无法写入新文件,下游命令失败退出
软件包损坏下载不完整、文件被截断dpkg-deb 读出非法数据,后续处理直接失败
权限/路径问题目标目录不存在或没有写权限解压中途失败,进程退出
dpkg 状态损坏dpkg 数据库锁冲突、半安装状态执行 dpkg 操作时自身状态冲突
父进程被终止SSH 断连、终端关闭导致 SIGHUP正在运行的 dpkg 连带被杀

其中我自己遇到概率最高的是“磁盘满”和“SSH 断连”。磁盘满导致数据写不进去,paste处理到一半,发现没有空间,直接退出,上游 dpkg-deb 收到 SIGPIPE,就报出了我们看到的错误。SSH 断连的情况更隐蔽,因为断连可能发生在任何时刻,dpkg 进程本身没有被显式 kill,但 SSH 会话关闭时,终端进程组会被发送 SIGHUP 信号,如果 dpkg 没有忽略这个信号,就同样会终止。

2.3 为什么在 SSH 远程安装时更容易遇到

这里要把两层概念拆清楚。dpkg-deb 报错是发生在远端服务器上的,而 SSH 客户端的packet write wait: connection to 192.168.1.11 port 22: broken pipe是你本机 SSH 进程发现的。很多时候,真实的顺序是这样的:

  1. 你通过 SSH 连上服务器,执行dpkg -i
  2. 安装过程比较久,中途网络闪断,或者 SSH 服务器因为空闲超时把连接关了。
  3. 本机 SSH 客户端发现连接断开,尝试往 socket 里写数据,结果得到 broken pipe 错误。
  4. 远端的 shell 收到 SIGHUP,把正在跑的 dpkg 进程也给带走了。
  5. 等网络恢复后你再看到终端,一堆错误输出里既有 ssh 的 broken pipe,也有 dpkg-deb 的 broken pipe。

所以,packet write waitdpkg-deb的 Broken pipe 经常会同时出现,但它们是两码事。前者是网络层的,后者是进程层的。排查时要先判断哪个在前,哪个在后,否则容易白折腾。

3. 分步排查与解决方案

3.1 第一步:检查磁盘空间与 inode

接到错误后,我建议第一个命令就是看磁盘,因为磁盘满是最常见也最致命的原因。执行:

df -h df -i

df -h看的是文件系统空间,df -i看的是 inode 数量。inode 是文件系统用来记录文件元数据的数据结构,即使空间没满,inode 满了也同样写不了文件。如果是 / 分区或者 /var 分区 100%,先清理再重试。常见的清理目标有:

# 清理 apt 缓存 sudo apt clean # 查看 /tmp 下有什么大户 sudo du -sh /tmp/* 2>/dev/null | sort -rh | head # 清理旧的日志 sudo journalctl --vacuum-time=3d

清理后重新df -h确认。如果磁盘和 inode 都没问题,再往下走。

3.2 修复 dpkg/apt 状态

磁盘没问题的情况下,dpkg 自身状态也可能会卡住。比如之前某次安装被杀,dpkg 的数据库里留下了半安装状态,后续操作就会异常。先跑一遍自动修复:

sudo dpkg --configure -a

这条命令会让 dpkg 尝试配置所有未配置完的包。如果它自己也报错,再翻日志:

sudo tail -100 /var/log/dpkg.log sudo tail -100 /var/log/apt/term.log

日志里经常能看到具体是哪个包、哪个文件在捣鬼。找到具体包名后,根据情况选择:

# 移除损坏但不需要的包 sudo dpkg --remove --force-remove-reinstreq 包名 # 重新配置安装一半的包 sudo dpkg --configure -a

这里要提醒一句,--force-remove-reinstreq属于强制手段,确认包没有价值后再用,别一上来就删,否则可能连带删掉依赖它的业务程序。

3.3 清理锁与临时目录

dpkg 本身有锁机制,防止多个进程同时操作。如果之前某个 dpkg 进程被杀,锁文件可能还留在原地,下面的命令卡在Waiting for cache lock。这时候不能直接乱删锁文件,先看谁在用:

sudo fuser -v /var/lib/dpkg/lock sudo fuser -v /var/lib/dpkg/lock-frontend

如果有进程输出,说明确实有 dpkg 在跑,等它结束。如果没有输出,说明锁已经被清理或残留了,这时可以安全删掉锁文件:

sudo rm -f /var/lib/dpkg/lock sudo rm -f /var/lib/dpkg/lock-frontend sudo rm -f /var/cache/apt/archives/lock

临时的 dpkg 解压目录/tmp也可能堆积了僵尸文件,顺手清理一下。但同样的,别无脑rm -rf /tmp/*,如果有其他程序在用,反而添乱。

3.4 校验并重新安装软件包

如果系统本身没问题,那大概率是软件包不完整。.deb 文件本质上是一个 ar 归档,里面包裹了debian-binarycontrol.tar.gzdata.tar.*。如果下载过程中文件被截断,dpkg-deb 自然没法正常解压。先校验下载的包:

md5sum upgrade-network-xxx.amd64.deb

拿到官方提供的校验值去比对,如果对不上,重新下载。也可以不校验,直接检查包内部结构:

dpkg-deb -I upgrade-network-xxx.amd64.deb

-I显示包的元信息,如果它报错,基本可以确认包损坏。如果正常,可以继续看文件列表:

dpkg-deb -c upgrade-network-xxx.amd64.deb

确认包没问题后,再安装。这里有个小经验:dpkg -i只安装不处理依赖,所以推荐用apt来装本地包,让 apt 自动处理依赖关系:

sudo apt install ./upgrade-network-xxx.amd64.deb

apt install ./这种写法在 Debian/Ubuntu/麒麟上都能用,会先做依赖分析,还能自动从源里补齐依赖,比裸dpkg -i稳得多。

3.5 解决远程连接中断问题

如果前面的排查都没有解决,你发现每次都是 SSH 断了之后才报错,那就要从远程会话本身下手。最常见也最推荐的做法是把长任务放进tmuxscreen里,这样即使本地断开,远端任务还能继续跑。tmux 的基本操作:

# 安装 tmux sudo apt install tmux -y # 创建新会话 tmux new -s install # 在会话里执行安装 sudo apt install ./xxx.deb # 临时离开会话(不影响任务) Ctrl+b d # 重新进入会话 tmux attach -t install

tmux里执行命令,SSH 断连不会影响安装进程,因为 tmux 会话是独立于当前终端的守护进程。你重连后tmux attach回去就能看到结果。如果不想用 tmux,也可以用nohup加日志:

sudo nohup dpkg -i upgrade-network-xxx.amd64.deb > /tmp/dpkg_install.log 2>&1 &

但 nohup 对进程组的保护不如 tmux 彻底,子进程可能还是会受影响,所以我还是推荐 tmux。

另外也可以调整 SSH 客户端和服务端的存活检测参数,减少断连概率。在客户端~/.ssh/config里加:

Host 192.168.1.11 ServerAliveInterval 30 ServerAliveCountMax 60

服务端在/etc/ssh/sshd_config里可以设置:

ClientAliveInterval 30 ClientAliveCountMax 60

这样 SSH 会定期互相发心跳,避免长时间无数据导致网络设备掐断连接。

4. 案例实操:麒麟系统升级包安装完整过程

4.1 环境说明与升级包信息

我这里的实际操作场景是一台麒麟高级服务器操作系统 V10 的机器,内核版本 4.19.x,系统刚从旧版本升级到新版本,需要安装一个内部发布的 “网络优化组件升级包”。包名我打个码,就叫net-optimize-2.0_amd64.deb。安装前我先确认了系统版本和 dpkg 版本:

$ cat /etc/kylin-release Kylin Linux Advanced Server release V10 (Tercel) $ dpkg --version dpkg 1.19.7 (amd64)

这类内部升级包通常发布在文件服务器上,我通过 SSH 用scp拉到服务器,然后直接dpkg -i,结果就遇到了开头的错误。

4.2 按顺序排查并最终定位原因

看到dpkg-deb: error: paste subprocess was killed by signal (Broken pipe)后,我本来以为是包的问题,但重传了一次还是同样报错,所以决定按流程排查。

先看磁盘:

$ df -h / Filesystem Size Used Avail Use% Mounted on /dev/mapper/kl-root 97G 97G 0G 100% /

好家伙,根分区 100% 占满。df -i虽然没满,但空间一点不剩,任何新文件都写不进去。dpkg 解压 deb 包时要在根目录和/tmp写入临时文件,磁盘满了,paste 子进程自然撑不住。

接下来清理空间:

sudo apt clean sudo rm -rf /var/log/journal/* sudo du -sh /tmp/* 2>/dev/null | sort -rh | head -5

清理完之后,磁盘占用降到 60%。这时再重新安装:

sudo apt install ./net-optimize-2.0_amd64.deb

这次没有任何报错,依赖也自动补齐了。我总结当时最大的坑是:升级前没有留意磁盘告警,导致一个简单的安装动作因为空间不足而失败。磁盘满导致的 dpkg 错误特别容易迷惑人,因为报错信息指向的是“信号”和“管道”,而不是“磁盘空间不足”。

4.3 用 tmux 保住长任务

这个案例还给了另一个教训。其实在第一次安装时,我是直接在 SSH 终端里敲的命令,安装过程还比较慢。第二次用 tmux 包裹以后,我才发现真正耗时的不是解压,而是依赖解析和配置脚本执行。之后我再做升级,都会先执行:

tmux new -s upgrade

然后在 tmux 会话里执行安装命令,这样就算本地网络抖动,也不会把远端安装进程带走。如果 SSH 断开重连后,用tmux attach -t upgrade就能回到现场,看到安装日志。这个习惯后来帮我避免了好几次“远程升级装到一半被断连搞成半包状态”的麻烦。

4.4 验证安装结果

安装完成后,不能只看终端没报错就喊完事,还是要验证一下包状态:

$ dpkg -l net-optimize Desired=Unknown/Install/Remove/Purge/Hold | Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad) ||/ Name Version Architecture Description +++-==============-============-============-=================================== ii net-optimize 2.0 amd64 Network Optimization Component

第一列ii表示安装成功且配置完成。再运行一下组件自带的版本命令:

$ net-optimize --version net-optimize version 2.0

确认没有其他异常。整个安装过程结束。

5. 常见问题速查与避坑清单

5.1 问题、原因、对策速查表

为了后面遇到同类问题能快速定位,我把常见组合整理成一个速查表。你可以收藏起来,或者打印出来贴在工位上。

现象可能原因解决动作
dpkg-deb: paste subprocess was killed by signal (Broken pipe)磁盘满 / inode 耗尽df -hdf -i,清理空间后用apt install ./xxx.deb重装
同样的错误,且包是新下载的软件包下载损坏md5sum校验,dpkg-deb -I验证包结构,重新下载
错误后 dpkg 卡住不动锁文件残留 / dpkg 状态损坏dpkg --configure -a,检查lock,必要时清理锁文件
伴随packet write wait: ... broken pipeSSH 连接中断用 tmux 或 nohup 执行长任务,配置 SSH 心跳保活
安装过程中报权限不足需要 root 权限使用sudo,或用root用户执行
反复重试依然报同样错误底层文件系统异常执行fsck前先备份,检查 RAID 状态或虚拟化宿主机磁盘

5.2 日常维护建议与个人心得

先说一条最实在的心得:不要在裸 SSH 会话里直接跑长时的软件安装或升级任务。互联网公司的服务器还好,办公网里的机器和异地机房设备经常会有网络抖动,一条 timeout 就能让你“摸不着头脑”。我现在只要是在远程操作安装超过两分钟的任务,都会主动加tmux。哪怕最后任务只跑了 30 秒,这个习惯也不能省。

再说磁盘监控。这个错误我最近几次遇到的根源都是磁盘满,而且不是 / 分区满,就是/var分区满。日志文件、容器镜像、apt 缓存都是吃磁盘的大户。建议至少给服务器配一个简单的磁盘告警,常用做法是cron定时跑一个脚本,超过 80% 就往群里发通知,或者直接部署 node_exporter + Alertmanager 那套监控。能早发现就不要等安装报错才意识到。

关于 dpkg 的强制选项,我想多说一句。网上很多资料会让你dpkg --force-all -i xxx.deb,这种“大力出奇迹”的方式在业务服务器上非常危险。它可能绕过依赖检查,装出半残状态,后续再装别的包时又冒出一堆诡异问题。能用apt install ./包名.deb解决的问题,就不要用--force硬上。

还有个小技巧:如果 dpkg 数据库已经处于半损坏状态,可以先备份一下:

sudo cp -a /var/lib/dpkg /var/lib/dpkg.bak

修复过程中如果操作失误,还能回滚。备份这块,老运维可能觉得多余,但在紧急时刻能救命的往往就是这一手。

另外,升级包安装前可以先查看包里的文件列表,确认不会覆盖掉正在使用的配置文件:

dpkg-deb -c net-optimize-2.0_amd64.deb

看看有没有/etc/下的文件,提前做好备份,比装完再后悔强。

最后再说回错误本身。Broken pipe 对开发者来说是个常见的信号,但对运维排查来说,它更像一个“症状”,而不是“病因”。遇到它,不要急着重装包,先检查磁盘、再检查 dpkg 状态、检查网络会话,按顺序来,大概率能找到真正的原因。这之后,我在麒麟系统上已经很少被这个报错吓到了。说实话,系统提示虽然看着吓人,但只要把上面几步走一遍,绝大多数场景都能在十分钟内解决。希望这篇总结也能让你别再为这个错误熬夜。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询