分类目录归档:心得总结

Linux 的缩骨功——openwrt 与其文件系统的原理探究

本文来自于我近年来折腾 openwrt 系统一大串疑问及探索过程收集整理。从约 2015 年开始买了 HG255D 刷 openwrt 入坑之后,openwrt 带给我疑问与答案探索的心路历程包括:

  • 还不太懂 Linux 的时候的时候,就已经玩了几年 dd-wrt 、 tomato 等第三方固件,刚接触 openwrt,发现竟和 dd-wrt,tomato 等差别这么大,自由度这么高,玩不久就干掉了 httpd 然后呆住了
  • 有些路由器刷 openwrt 需要 “不死 uboot”,只要 uboot 尚存,刷什么固件都可以,这不就是 PC 的 BIOS 或 UEFI,有个 U 盘就能装系统么
  • 刷了 openwrt 以后,因为要装的软件很多(尤其是越来越多的 go 写就的软件),路由器厂家为了成本节省而内置的存储空间不够,因此带 USB 接口可插 U 盘的路由器拥有了上 GB 额外存储空间的机会,不过为什么要把 U 盘挂载成软件安装的地方,这么复杂?…

到了 2024 年,当一批联发科 filogic 芯片带动的、能刷 openwrt 的 WiFi6 路由器兴起后,通过不断的踩坑、填坑、折腾,终于对脱胎于 Linux 但又因嵌入式的桎梏而做出相当多改造的 openwrt 及其文件系统有了更深层的一些认知。

以 AI 为师,是当下最流行的一件事。在踩坑的过程中,通过告诉 AI“我不要操作过程我要知道原理”,问出我最好奇的事:x86 Linux 的文件系统以 / 为根目录,以 openwrt 为代表的嵌入式 Linux 系统,在一块成本低廉的闪存芯片上为什么有着着复杂的 “分区”:uboot 、只读 SquashFS 、 overlay 、 JFFS?

Google gemini 解答如下:

[ U-Boot ]路由器通电后最先执行
[ U-Boot Env/Config ]U-Boot 的配置参数
[ Linux Kernel ]U-Boot 加载并运行它
[ SquashFS (只读 rom) ]系统的核心,出厂固件就固定在这里
[ JFFS2/UBIFS (可写 /overlay) ]你所有的更改、安装的软件都写入这里 (默认情况下)
[ 其他分区 (如日志, 恢复等) ]optional
「你可以想象路由器闪存芯片被逻辑上划分成上面这样(具体名称和大小会因路由器型号而异)

第一,位于内置存储最前端的、类似于 UEFI 的、负责引导操作系统的 bootloader 是必不可少的。磕碜一点的 bootloader 带 tftp 加载功能,须刷机的 PC 端以 tftp 协议传入固件;豪华一点的 bootloader 带 web GUI 可上传并刷入固件,甚至带简单的 DHCP 以增强用户体验。刷入 openwrt 系统时一般会避开这一部分,如果这一部分也被刷坏,那么在拆机 JTAG 介入之前,那可就真的砖了。

第二,和安装在 x86 设备上传统 Linux 系统有着本质不同的是,嵌入式 openwrt 系统必须有种 “RESET 重置” 后,能恢复到某种固定的 “出厂设置” 状态的 feature,像 PC 或 server“格盘” 后只剩空荡荡的硬盘是不可接受的。因此一个 “只读” 的文件系统、在 “RESET 重置” 后能恢复到一个固定状态(譬如默认网段为 192.168.1.0/24,默认网关 192.168.1.1 这样的出厂默认值)的文件系统是必不可少的。这就是上述的只读 ROM,位于 SquashFS 文件系统的部分。

第三,我们对路由器所做的任何设置,如默认网段、网关、防火墙策略、 WAN 口 PPPoE 账号密码、第三方软件等配置,都会存储在挂载为 /overlay 的 JFFS2/UBIFS 文件系统中(/overlay/etc)。关键在于,这里的 /overlay ,既可以是路由器刷入 openwrt 后的剩余空间,又可以是 USB 外置存储。

我是怎么知道 openwrt 的个性化配置都存在 /etc 中的…… openwrt 的配置备份并下载后是个 backup.tar.gz 文件,打开后就知道了……

也就是说,如果未插入外置存储,/overlay 将会是路由器闪存中紧跟着固件之后的一片闪存空间;插入 USB 存储并正确识别、挂载为 /overlay 后,/overlay 就会在外置存储中。

而此时,运行中的 openwrt 的 /etc 是一个动态的、由 OverlayFS 合并生成的虚拟目录/overlay/etc 是一个实际存在于可变存储区(参考上一节,无 U 盘时是路由器闪存的 JFFS2/UBIFS,有 U 盘时是优盘)上的物理目录,专门用来存放用户对系统配置所做的所有个性化更改。出厂设定(默认 /etc 内容)则存储于只读的 SquashFS 中,平时被上层(即 Overlay 的字面意思)遮蔽而不生效,只有 RESET 重置后,也就是上层遮蔽都没了的时候,才把固化在只读 SquashFS 分区中的原版 /etc 拿出来生效。

这种聪明的办法可以解决固定的出厂设定问题,也可以使我插了 U 盘并用作 /overlay 的路由器有了一个保底方案,即内置的较为紧张的 JFFS2/UBIFS 存储中存一份路由器能正常运作的 “最小配置”,用于日常使用的白菜价 U 盘外置存储则承担日常 /overlay 职能,装了一堆 ipk 软件包。即使在 U 盘本身或挂载出现问题的情况下,也可以 fallback 回落到能满足最基本上网需求的 “最小配置” 。

我常惊叹的 Linux 的 “缩骨功” 厉害之处在于,不仅能像被 “缩小灯” 照过一样装入螺蛳壳般的嵌入式设备存储空间中,还能以 “柔软的身段”——灵活的文件系统、挂载方式去实现 “用户能一键 RESET 恢复出厂设置” 这样的需求。

从 iso 到 qcow2 的坎坷之路

之前在云上部署下一代防火墙(NGFW)的时候,须订购云主机并安装指定的 NGFW 产品系统镜像。大致步骤:将该 NGFW 的镜像包(qcow2 格式)先上传至云内对象存储,再导入至自定义操作系统列表,就可以灌装到云主机中。再把 VPC 内的其他主机路由表下一跳指向该主机,就可以实现 NGFW 对 VPC 内流量统一管理。

此次,某客户在订购了裸金属服务器后,须安装指定的某版本操作系统,但不在标准操作系统选单中,于是紧急以 iso 安装包起手,拿出指导帮助文档,利用手头的环境边走边学 qcow2 镜像的制作过程,是为记。

手头 VMware 的虚拟机中已有 CentOS 7.9,但没有 GUI,制作工具 virt-manager 是底层 python 、表层 GTK 或 Qt 写就的 GUI 工具,那么纯命令行的系统则不行。于是第一步便是把 CentOS 的 GUI 给出弄出来:

yum groupinstall "GNOME Desktop" "Graphical Administration Tools" -y
systemctl get-default 可以看到当前默认的模式为 multi-user.target
systemctl set-default graphical.target 修改为图形界面模式


将 CentOS 7.9 GUI 设置好之后,virt-manager 装好之后,一通折腾,最后仍然以缺 python 的 gi 库告终,摊手。点亮斜门初级技能:把命令行系统变出 GUI 。

最后还是想到了自己那台万能的 N100 小主机。含 GNOME 的 Manjaro,各方面都符合要求,于是准备用其从 iso 制作 qcow2 。 sudo pacman -S virt-manager,并把其他缺失包一一装好后,又出现了新的问题:Virtual Machine Manager 里面没有 QEMU,只有 LXC 。

搜了下,LXC(Linux Containers)是基于容器的虚拟化,KVM(Kernel-based Virtual Machine)是更底层的内核级虚拟化。那为什么没有 KVM 呢,问了下最近觉得技术方面比较给力的 Google Gemini,回答如下:

在使用 Manjaro 安装 virt-manager 后,出现 LXC 而没有 QEMU,通常意味着 virt-manager 默认连接到了 LXC hypervisor,而不是 QEMU/KVM 。
这里有几个常见的原因和解决方法:

1. 确保你已经安装了 QEMU 和 Libvirt 相关的必要包。在 Manjaro 上,这通常包括 libvirt virt-manager 以及可选的 KVM 模块 qemu-arch-extra;
2. libvirtd.socket 或 libvirtd.service 的启用问题,要确保 libvirtd.socket 或 libvirtd.service 已经启动并启用;
3. 用户权限问题:用户需要添加到 libvirt 用户组才能无密码地使用 virt-manager 连接到 QEMU/KVM;

之后又给出了一些复查命令。

照做之后,果然在 Virtual Machine Manager 中出现 QEMU/KVM 。之后就是在 virt-manager 的 QEMU 中新建虚拟机,这里的界面很像 VMware 的硬件配置界面。

把 iso 文件导入虚拟 CDROM,简单配置下硬件,Begin Installation,得到的磁盘镜像文件就是 qcow2,类似于 VMware 的 vmdk 。

最后 qemu-img convert -c -O 把镜像文件压缩一下就行。写到这里,我突然想到:在 VMware 里制作 vmdk,用 qemu-img convert 直接转成 qcow2 呢,应该也可以吧。如果绕路了,那也一定是为了看风景。:D