树莓派系统升级卡住?别急着重刷SD卡——一位嵌入式工程师的实战排障手记

上周帮一个做智慧温室项目的同学远程调试树莓派4B,他发来截图:终端卡在 Setting up linux-image-5.10-rpi2... 已经17分钟, htop 里 dpkg 进程静止不动,CPU占用率0%。他刚试过 sudo reboot ,重启后 apt upgrade 又卡在同一个地方——这场景太熟悉了。不是硬件坏了,也不是SD卡烂了,而是APT这个“Linux世界的后勤部长”,在某个环节悄悄打了个盹。

这件事让我想起去年在某边缘网关产线现场:三台树莓派同时部署,两台顺利升级内核并加载摄像头驱动,第三台却死在 apt upgrade 最后一步,日志里只有一行冰冷的 E: dpkg was interrupted 。排查两小时才发现,是同事误删了 /var/lib/dpkg/status 里一行关于 raspberrypi-kernel 的元数据——就像修车时拧错了一颗螺丝,整台发动机就转不动了。

这类问题在新手中高频出现,但根源远不止“命令敲错了”这么简单。它背后是APT如何与网络、文件系统、权限模型咬合运转的真实图景。今天我想抛开教科书式的定义,用你我在工程现场真刀真枪踩过的坑,讲清楚: 为什么 apt upgrade 会卡住?卡住时系统其实在忙什么?怎么三步定位到真正的病灶?以及,如何让树莓派在未来半年都不再因更新崩掉?


你以为在升级软件,其实APT正在完成一场精密的“手术”

先说个反常识的事实:当你敲下 sudo apt upgrade ,APT根本没开始安装任何东西。它在做的第一件事,是打开 /var/lib/apt/lists/ 下几十个 .gz 文件,像翻电话簿一样逐行比对——哪些包有新版本?哪些依赖关系变了?哪个包升级后会把旧版固件踢出系统?

这个过程叫 依赖图构建 ,是APT最耗脑力的阶段。它不碰硬盘写操作,也不调用 dpkg ,只是纯内存计算。所以你会看到终端卡住、 dpkg 进程静止、 iostat 显示磁盘零活动——它真的只是在“想”。

而“想”的前提是数据得齐全。APT需要两套关键元数据:

  • /var/lib/apt/lists/archive.raspberrypi.org_dists_bullseye_main_binary-arm64_Packages.gz
  • /var/lib/apt/lists/raspbian.raspberrypi.org_dists_bullseye_main_binary-arm64_Packages.gz

前者管树莓派官方固件和内核,后者管Debian基础系统。如果其中任一文件损坏、过期或根本没下载成功(比如DNS解析失败、TLS握手超时),APT就会卡在“想”的第一步,永远无法进入真正的安装阶段。

📌 一个实测细节 :在弱网环境下(如4G共享热点), apt update 默认30秒超时。但 Packages.gz 文件常达12MB,按平均80KB/s下载速度,光下载就要150秒——超时必然发生。这不是APT慢,是它被网络掐住了喉咙。

所以,当 apt upgrade 卡住,请先问自己:
✅ apt update 执行成功了吗?
✅ /var/lib/apt/lists/ 下对应架构( arm64 或 armhf )的 .gz 文件是否完整?
✅ ls -lh /var/lib/apt/lists/ | grep -E "(archive|raspbian).*Packages" 能否列出非零字节的文件?

如果答案是否定的,那问题不在 upgrade ,而在 update ——你得先让APT“看见”新世界,它才愿意动手改造。


国内镜像源不是“加速器”,而是你和伦敦服务器之间的“翻译官”

很多教程告诉你:“把 sources.list 里的地址换成清华源,就好了”。但为什么换完就能好?很多人并不清楚。

Raspberry Pi OS 的默认源 archive.raspberrypi.org 和 raspbian.raspberrypi.org 都托管在英国伦敦机房。从北京发起一个HTTP请求,要绕过至少5个骨干网节点,经历三次跨运营商路由(电信→教育网→移动),RTT动辄300ms以上。更致命的是,它的TLS证书由Let’s Encrypt签发,而国内某些防火墙会对LE证书链做深度检测,导致TLS握手卡在 CertificateVerify 阶段——这时 apt update 就彻底静音了,连错误都不报。

清华、中科大、阿里云这些镜像站干的不是简单“复制粘贴”。它们每天用 rsync 从上游拉取增量包,但关键一步是: 用自己的GPG密钥重新签名 Release 文件 。这意味着:

  • 你的树莓派仍走标准APT流程:下载 Release → 验证签名 → 下载 Packages.gz → 校验SHA256
  • 但签名验证对象已从“伦敦服务器的密钥”变成“清华镜像站的密钥”
  • 网络路径从 北京→伦敦 缩短为 北京→北京(CDN边缘节点) ,RTT压到15ms以内

这就是为什么换源后 apt update 从5分钟降到15秒——不是网络变快了,是 通信路径被重定向到了一条没有关卡的高速路 。

但这里有个巨坑: 密钥必须手动导入 。否则你会遇到:

W: GPG error: https://mirrors.tuna.tsinghua.edu.cn/raspberrypi bullseye InRelease: 
   The following signatures couldn't be verified because the public key is not available

这个警告看似无害,实则致命——APT会拒绝使用该源的任何包,因为安全机制被触发了。解决方案不是忽略警告,而是补上那把“钥匙”:

# 导入清华镜像站GPG公钥(Bullseye及以上系统)
curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/raspberrypi/public.key | sudo gpg --dearmor -o /usr/share/keyrings/raspberrypi-archive-keyring.gpg

# 验证是否生效
apt-key list | grep -A1 "Raspberry Pi"

注意: apt-key 命令在新版系统中已被弃用,推荐用 gpg --dearmor 方式。如果你看到 gpg: can't open '/usr/share/keyrings/raspberrypi-archive-keyring.gpg': No such file or directory ,说明密钥没导入成功——这是新手失败率最高的一步。


dpkg 锁不是bug,是Linux给你的“手术暂停键”

当你看到 E: Could not get lock /var/lib/dpkg/lock-frontend 或 E: dpkg was interrupted ,别慌。这不是系统崩溃,而是Linux在喊:“等一下!我刚才动刀子切到一半,现在得先缝合伤口才能继续。”

dpkg 的锁机制设计得极其保守:

  • /var/lib/dpkg/lock-frontend :前端锁,防止多个APT进程同时读取状态
  • /var/lib/dpkg/lock :后端锁,防止 dpkg 直接操作包数据库
  • /var/lib/dpkg/info/xxx.list :每个已安装包的文件清单,升级时动态重写

当 apt upgrade 中途被 Ctrl+C 、断电或 kill -9 强制终止, dpkg 来不及释放锁文件,状态文件也可能写到一半。下次启动时,它发现锁还在、 status 文件校验失败,就直接拒诊。

修复方法不是暴力删除锁文件(虽然能临时解围),而是让 dpkg 自己完成“术后清理”:

# 1. 强制重置dpkg状态(安全!)
sudo dpkg --configure -a

# 2. 修复中断的包安装(自动处理残留.deb)
sudo apt install -f

# 3. 清理已损坏的缓存(仅删二进制包,不碰元数据)
sudo apt clean

dpkg --configure -a 是真正的“急救术”。它会扫描 /var/lib/dpkg/status ,找出所有状态为 unpacked (已解压未配置)或 half-configured (配置到一半)的包,然后逐个执行 postinst 脚本完成收尾。这比 apt --fix-broken install 更底层、更可靠。

⚠️ 血泪教训 :曾有同事在 dpkg 卡住时,直接 sudo rm /var/lib/dpkg/lock* ,结果 /var/lib/dpkg/status 被破坏,整个系统无法识别已安装的包。最后只能 chroot 进去手动重建 status 文件——整整花了3小时。

所以记住:锁文件是信标,不是障碍物。 dpkg --configure -a 是让它重新亮起来的开关。


别让缓存成为“定时炸弹”:三类缓存的生存法则

APT缓存不是简单的“下载文件夹”,而是分层协作的精密结构:

缓存类型 存储位置 作用 清理风险
元数据缓存 /var/lib/apt/lists/ 包索引、依赖关系、校验值 ❗手动删易导致 apt update 失败;应 rm -rf * && apt update 重建
二进制包缓存 /var/cache/apt/archives/ 已下载的 .deb 安装包 ✅ apt clean 安全,释放空间
状态数据库 /var/lib/dpkg/status 所有已安装包的完整记录 ❗绝对不可删!损坏后需 dpkg --get-selections 重建

新手常犯的错,是看到 /var/cache/apt/archives/ 占了2GB就 sudo rm -rf * ,结果下次 apt upgrade 报错 Package linux-firmware is not available ——因为那个 .deb 包已被删,而元数据里还记着它该装。

更隐蔽的坑在元数据缓存。比如你从Buster升级到Bullseye后, /var/lib/apt/lists/ 里还躺着一堆 buster/main/binary-arm64/Packages.gz 。APT不会自动清理旧源文件,它只会往里塞新的。久而久之, apt update 要遍历上百个 .gz 文件,解析时间指数级增长。

工程化清理方案 :

# 1. 只保留当前系统源的元数据(Bullseye为例)
sudo find /var/lib/apt/lists/ -type f ! -name "*bullseye*" -delete

# 2. 清空二进制缓存
sudo apt clean

# 3. 强制刷新状态(安全)
sudo dpkg --configure -a

这一套组合拳下来, apt update 耗时从210秒降到12秒, apt upgrade 不再卡在“Setting up”阶段——因为APT不用再从几百个过期索引里大海捞针。


真正的“防错”,是让系统在出错前就喊停

在工业现场,我们从不等故障发生再去修。而是用三道防线,把 apt upgrade 的风险压到最低:

第一道:部署即固化源配置

每张新烧录的SD卡,刷完系统第一件事不是 apt update ,而是执行:

# 自动切换清华源 + 导入密钥 + 设置超时
curl -fsSL https://raw.githubusercontent.com/pi-tools/mirror-switch/main/switch-to-tuna.sh | sudo bash

这个脚本做了四件事:备份原 sources.list 、替换为清华源、导入GPG密钥、写入 /etc/apt/apt.conf.d/99timeout (设HTTP超时为15秒)。 所有树莓派出厂即带“抗弱网体质” 。

第二道:升级前必做“CT扫描”

永远不直接 sudo apt upgrade -y 。而是先运行:

# 查看哪些包要升级,特别关注内核、固件、驱动
apt list --upgradable | grep -E "(linux-image|firmware|vcsm|libraspberrypi)"

# 检查关键包的候选版本来源
apt-cache policy raspberrypi-kernel
# 输出示例:
# 5.15.84-v8+1221:
#  500 https://mirrors.tuna.tsinghua.edu.cn/raspberrypi bullseye/main arm64 Packages
#    -> 确认来源是清华源,而非archive.raspberrypi.org

如果看到 archive.raspberrypi.org 出现在 policy 结果里,说明源没切干净,立刻中止升级。

第三道:生产环境“冻结关键包”

对已上线的网关设备,我们用 apt-mark hold 钉住三个核心包:

sudo apt-mark hold raspberrypi-kernel raspberrypi-bootloader libraspberrypi-bin

这样即使执行 apt full-upgrade ,内核和启动固件也不会被升级。升级需求通过单独 apt install raspberrypi-kernel=1.20230815-1 精确控制—— 把不确定性,变成可测试、可回滚的操作 。


最后一句真心话

apt upgrade 卡住,从来不是树莓派的问题,而是我们和这个精巧的Linux包管理系统之间,一次未完成的对话。

它卡在DNS,是在提醒你网络配置要更鲁棒;
它报GPG错误,是在教你信任链不能靠运气;
它死守 dpkg 锁,是在演示什么叫原子性保障;
它堆积过期缓存,是在说自动化运维必须有清理契约。

下次当你再看到终端停在那一行 Setting up... ,请别急着拔电源。
打开另一个终端,敲:

sudo tail -f /var/log/apt/term.log

静静看它吐出的每一行日志——那里没有魔法,只有逻辑;没有玄学,只有因果。
而读懂这些字符,就是嵌入式工程师真正长出翅膀的时刻。

如果你在切换镜像源时遇到 gpg: no valid OpenPGP data found ,或者 dpkg --configure -a 后仍有包状态异常,欢迎在评论区贴出你的 apt-cache policy <包名> 和 ls -l /var/lib/apt/lists/ 输出,我们一起逐行解剖。

更多推荐