dnf 的前世今生

已修订更新于

首发于

dnf 的前世今生

这篇文章主要介绍 dnf 的前世今生:它不是凭空出现的,而是叠在 RPM 几十年演进之上的一层。

先记一条主线:

RPM  —— 管本机:装了什么、文件落在哪、本地依赖元数据
  ↑
yum / dnf —— 管远程:仓库索引、拉包、版本依赖图、冲突求解

dnf 原本是 yum 的升级版本。今天的 dnf 仍然基于 RPM:RPM 负责管理本地包数据库;dnf 负责索引远程仓库,并在把远程包装到本机时,做版本依赖的图分析、用算法优化冲突一类问题。同时,它也负责在本机视角管理「有哪些远程仓库」。


一、先有 RPM

为什么不是一步到位的「远程包管理器」?因为九十年代要先解决的是:本机软件怎么记账。

1. 本机记账

九十年代中期,Linux 发行版刚开始把软件做成「包」。Red Hat 这边先后试过几代工具:

  1. RPP(Red Hat Package Manager 的前身之一)
    最早的 Red Hat Linux 预览版 / 1.0(约 1994–1995)用的是 RPP,还不是后来的 RPM。

  2. PMS / PM
    Rik Faith 等人在 BOGUS 发行版上做了 PMS;后来 Faith 与 Doug Hoffman 又为 Red Hat 做过 PM。这些工具已经有「一条命令装/卸」「安装前后脚本」「校验」「查询」等想法,但数据库设计偏弱,对多架构支持也不够。

  3. RPM(1995,Marc Ewing & Erik Troan)
    总结前几代教训后,第三代方案就叫 Red Hat Package Manager。Red Hat Linux 2.0 起正式采用。早期 1.x 用 Perl 快速落地;随后整套用 C 重写,并把包数据库格式重新设计,查询更快、也更可靠。名字后来变成递归缩写:RPM Package Manager。

这一阶段要解决的核心问题很朴素:

答案是:给本机维护一份专用的包数据库(传统路径在 /var/lib/rpm)。
RPM 把「包头元数据 + 已安装文件清单」当成系统真相源;查询(rpm -q)、校验、安装、卸载都围绕这份库转。

这就是今天分工的底色:RPM 从诞生起就是「本机账本」,不是「网上商店」。

2. RPM 不做的事

有些能力 RPM 故意不做,也长期做不好——正因如此,上面才需要再叠一层。

RPM 能装一个本地 .rpm 文件,也能读出它声明的依赖;但早期它不会自动帮你:

网络还慢、仓库规模也小的年代,这可以接受:管理员自己下包、自己理依赖。等发行版包数量涨到成千上万,再靠人手拼依赖就不可持续了——于是上面必须再叠一层「仓库客户端 + 依赖求解器」。


二、从 yum 到 dnf

中间那一层补上了远程仓库与自动依赖,再由 dnf 把求解引擎现代化。

1. yum

给 RPM 补上「远程仓库 + 自动依赖」。

大约 1999–2001,面向 PowerPC / Mac 的 Yellow Dog Linux 做了 YUP(Yellowdog Updater):从 RPM 头里抽依赖,拼依赖树,自动化安装与维护。

2002 年前后,Duke 大学物理系系统管理员 Seth Vidal(后来还有 Michael Stenner 等人)觉得 yup 不够快、也不够通用,于是重写成 yum——Yellowdog Updater, Modified。yum 仍然操作 RPM 格式的包,仍然把最终安装写进 RPM 数据库,但把「仓库元数据、下载、依赖解析」收到自己这边。

之后 yum 成为 Fedora / RHEL 系的默认高层包管理器,几乎成了「人们口中的包管理」——尽管底层记账的仍是 rpm。

2. dnf

yum 的现代化重写,全称 Dandified YUM。

yum 用久了,暴露出几类结构问题:依赖求解偏迭代、内存与速度吃紧、Python API 长年有机生长、难给 Anaconda / PackageKit 等非 Python 客户端复用。

2012 年左右,项目从 yum 3.4 fork 出 dnf,核心变化包括:

Fedora 从 Fedora 22 起把 dnf 定为默认;RHEL 8 一代也切到 dnf。命令行刻意兼容大部分 yum 习惯,所以很多人感觉「还是那个 yum」,但引擎已经换代。

于是今天的图景是:

层 管什么 典型命令
RPM 本机包数据库、本地 .rpm 的装/卸/查/验 rpm -qa、rpm -ivh foo.rpm
dnf(前身 yum) 远程仓库索引、本机仓库配置、跨包依赖图与冲突、事务历史 dnf install、dnf upgrade、dnf repolist

dnf 没有取代 RPM;它是站在 RPM 之上的编排层。真正改动系统文件、改写 /var/lib/rpm 的,最后仍落到 RPM 事务。


三、职责切分

现在为什么「必须是这样」?一次安装把两层各自的活摊开就清楚了。

可以按数据流理解一次 dnf install foo:

  1. dnf 读本机仓库配置(例如 /etc/yum.repos.d/),刷新或使用缓存的仓库元数据;
  2. dnf 结合「已安装集合」(来自 RPM DB)与「仓库候选集合」,建依赖图,求解要装 / 升级 / 淘汰哪些包;
  3. dnf 下载选定的 .rpm;
  4. RPM 执行实际安装:解包、跑脚本、更新本机包数据库。

所以:

查询也会分成两套气质不同的入口,对应「给人看」和「给脚本用」:

本地侧同理:日常用 rpm -q / rpm -qa 查账本;需要机器可读字段时同样靠 queryformat。


四、查询与写入

特别值得一提:dnf(以及底层的 rpm)把「查」和「写」当成两套原则——查可以放宽,写必须收紧。

1. 查询

普通用户和管理员都可以。

只读不改系统,所以不必提权。这对「看看有没有某库」「脚本探测已装版本」很重要。

2. 写入

改的是系统级状态,必须管理员。

一旦要:

就涉及系统级写入:往 /usr、/etc、/var 落文件,并更新 RPM 数据库。这和「普通用户在家目录装东西」不是一类操作,因此 dnf 的写入类命令、rpm 的写入类命令,都需要管理员权限(root 或等价的提权)。

可以记成:

读(query / list / repoquery / rpm -q)→ 用户可做
写(install / remove / 改 rpmdb)    → 必须管理员

这不是 dnf 独有的「刁难」,而是 RPM 从一开始把「已安装软件」定义成整机共享状态的自然结果:账本在系统目录里,写入账本就等于改系统。


五、收束

回看整条时间线:

  1. 1995 前后:RPM 在 RPP/PMS/PM 的教训上长出来,核心是可靠的本机包数据库。
  2. 2000 年代:包与仓库规模爆炸,yum 补上远程索引与自动依赖;RPM 继续只管本地落地。
  3. 2010 年代至今:dnf 用更好的求解器与库边界接过 yum,但分层没变——RPM 记账,dnf 找货与算依赖。

所以今天用 Fedora / RHEL 系时,不必把 dnf 和 rpm 对立起来:日常装软件用 dnf;排障、查本机真相、处理单个 .rpm 时仍常落到 rpm。理解「谁管本地账本、谁管远程与图」,再看 list / repoquery 以及「能查不能乱写」的权限模型,整条链路就顺了。

评论

可对整篇留言,或划选正文中的句子后评论该段。

登录后即可发表评论。

  • 还没有公开评论。
↑