遇到 Ubuntu 22.04 上 systemd 升级失败,dpkg 直接甩出 "Invalid cross-device link" 的时候,说实话我第一反应是有点懵的。干这行久了,dpkg 的报错翻来覆去无非是锁冲突、依赖不满足、配置文件覆盖确认这几类,但 “无效的跨设备链接” 这种错误码,一年到头也未必能撞上一次。它不像普通的依赖问题,靠搜索引擎随手一搜就能找到现成答案,它背后往往藏着一个更隐蔽的系统状态——这次排查下来,问题根源锁定在 bind mount 上。
这篇文章是我的完整排错记录,从报错现场、原理分析,到一步步解除挂载、修复 dpkg 状态、最终完成 systemd 升级,全程可复现。如果你手里正好有 Ubuntu 22.04 或其他基于 dpkg 的发行版遇到类似问题,可以直接照着排查。
1. 故障现场:systemd 升级时 dpkg 抛出的奇怪错误
1.1 重启 apt 升级后的完整报错
先说下我当时的状态。那台机器是跑内网服务用的 Ubuntu 22.04 LTS,平时很稳定,也没动过什么大手脚。例行维护时我执行了 sudo apt update && sudo apt upgrade,apt 提示有一批包可以更新,其中就包括 systemd 及其相关库(libsystemd0、systemd-timesyncd、udev 等)。当时没多想,直接确认安装。
下载和解包阶段都很正常,结果到了配置阶段,终端哗哗滚出一段红字:
code复制Setting up systemd (249.11-0ubuntu3.12) ...
dpkg: error: unable to rename '/usr/lib/systemd/systemd.dpkg-new' to '/usr/lib/systemd/systemd': Invalid cross-device link (18)
dpkg: error processing package systemd (--configure):
installed systemd package post-installation script subprocess returned error exit status 2
Errors were encountered while processing:
systemd
E: Sub-process /usr/bin/dpkg returned an error code (1)
注意看报错里的关键字:unable to rename 和 Invalid cross-device link。这不是“文件被占用”或者“权限不够”这类普通错误,而是 Linux 内核返回的 EXDEV 错误码。说白了就是,系统不允许把某个文件从一个挂载点直接改名挪到另一个挂载点去。
当时我盯着报错愣了几秒,第一反应是磁盘满了?跑了下 df -h,空间充足。第二反应是 /usr 分区是不是坏了?查了下 dmesg,也没有 IO 错误或者文件系统异常。直觉告诉我,事情没那么简单。
1.2 这条错误的特殊之处在哪
如果你平时不怎么跟底层机制打交道,可能会觉得“跨设备链接”听起来像是两块硬盘之间拷贝文件的问题。但实际上,Linux 的 rename 系统调用对“跨挂载点”是有严格限制的。即便源文件和目标文件在同一个磁盘分区上,只要它们属于不同的挂载点,rename 就会失败,返回 EXDEV。
这个细节非常关键。因为 bind mount 恰好会创建“新的挂载点”,哪怕它背后指向的还是同一个物理磁盘、同一个文件系统。打个比方:你有一个文件夹 A,用 bind 的方式把它同时挂载到路径 B,那么 A 和 B 看起来内容完全一样,但在内核视角里,它们是两个独立挂载点。当某个程序尝试把 A 下的文件 rename 到 B 下,内核会直接拒绝执行。
dpkg 安装包的时候,会先在目标目录生成一个 .dpkg-new 的临时文件,然后再通过 rename 把它变成正式文件。这个机制是为了保证安装的原子性,避免写到一半程序崩溃导致文件残缺。正常情况下,临时文件和最终文件在同一个挂载点里,rename 是原子的,瞬间完成。可一旦目标路径被 bind mount 挡了道,或者说 dpkg 的工作目录和目标路径跨了挂载点,rename 就必然炸。systemd 这种核心包在 /usr/lib 下文件数量多,任何一个关键路径被 bind
