给 OrangePi 3 LTS 装上 Arch
前几天(?), 整理东西的时候, 新鲜出土了一个上古时代的产物, 一块 Linux 开发板, 没错, 类似于 Raspberry Pi 一样的东西, 一般也被称为单板电脑 -- 一块开发板就是一个完整的电脑, 除了没有外设

相应的, 在科技圈, 便携往往也是性能孱弱的代名词. 像我手上这块, 使用的是四核 Cortex-A53. 这大概是个什么水平呢? 可能还没有你家的路由器性能高. 使用 sysbench
跑分, 单核只有可怜的 900 分. (而承载这个网站运转的那台服务器, 单核跑分有 5300. 甚至比我自己的电脑还高)
因此, 并不推荐你去买这个, 并且社区资源太少了, 正的又需要的话可以去看看 Raspberry Pi 至少他有一个强大的社区, 可以提供相当多的学习资源. (从板子上的灰也多少可以看出一点端倪)

原理
首先, 这个东西的运行原理跟你常用的 X86_64 (也称 AMD64, Intel 管他叫 Intel 64/EM64T)[1] 架构的电脑还是有很大的区别的, 因为指令集授权问题, 实际上没有几家公司可以生产 X86_64 架构的处理器. 基本上一只手都可以数的过来, 1. AMD; 2. Intel; 3. 上海兆芯; 4. 海光信息 (并且后两者生产的 CPU 在性能上几乎不可用). 再加上 CISC 架构 的能效问题, 这个领域基本上都是 ARM 架构 的天下. (当然, 还有 RISC-V, 但是他太小众了)
但是呢, 因为 ARM 除了服务器领域和 Windows on ARM, 基本上都不用那个在 X86_64 上相当好的 UEFI, 大概主要还是因为他的集成度过高 (毕竟芯片都叫做 SoC 了), 没有统一的总线协议, 做不到 "硬件自发现". 毕竟芯片厂直接对接手机厂, 谁也不想做通用兼容, 这群人太坏了!.
那么 ARM 架构的 Linux 是如何启动的呢? 一般来说, 这类硬件在上电之后会固定运行烧录在 ROM 上的固定的启动代码, 在存储介质中的固定位置加载 Bootloader; 然后 Bootloader 接手启动流程, 加载 Linux 内核和 DTB (设备树, 负责直接告诉内核, 这个硬件上有哪些设备); 然后启动 Linux 内核并转交控制权; 然后 Linux 内核挂载根文件系统, 启动第一个用户态程序 (一般来说是 /sbin/init, 如果使用 systemd 的话, 通常是 systemd 会接管); 然后从这个用户态程序拉起整个用户态空间 (网络, 图形界面等).

如果里手里有 Linux 设备的话, 也可以直接查一下, 是谁在接管 /sbin/init.
/sbin/init: symbolic link to ../lib/systemd/systemd
那么, 综上所述, 一个可供开发板刷写的镜像主要由四部分组成, 启动方式 (一般来说是 U-boot), Linux 内核, 设备树文件, 一组基础软件包.
制作镜像文件
那么我们要怎么找这四个组成部分呢? 一般来说, 硬件厂商会直接提供所有需要的组成部分. 比如说我这块开发板就提供了基于 Ubuntu 制作的镜像, 和完整的镜像制作套件.
首先, 我们回顾一下, Linux 发行版的边界在哪? Arch Linux 有一个很火的衍生版, CachyOS. 他四舍五入跟 Arch Linux 没有什么区别, 使用同样的包管理器, 系统组成也几乎一模一样, 甚至, 海岸可以直接在 Arch Linux 上加入 CachyOS 的软件源, 变成 CachyOS 风味 Arch.
但是, 怎么做之后, 所有的软件包都变成由 CachyOS 提供的, 那么你的这条 忒修斯之船 还算是 Arch 吗? (甚至, 你还可以安装 cachyos-settings 和 cachyos-hooks 这两个包, 把他完全 Cachy 化)
(我之前也加入了 CachyOS 的软件源体验了一下, 对于我的电脑, 他大概有 5% 左右的性能提升. 但是, 他有一个巨大的 "缺陷", 对于一些更新频繁的软件, CachyOS 的软件源永远落后一到两个版本, 用不到最新的软件, 你知道这对一个 Arch 用户来说有多大的心理伤害嘛)

好像扯远了, 总之, 把系统的软件包管理器和软件包换成 Arch 的 pacman, 他就是 Arch Linux 了.
对应到上面说的, 镜像的四个组成部分, 就是最后那个 "一组基础软件包" 了. 那么要如何替换呢? 这些基础的预装组件一般都是 RootFS 提供的, 跟他的名字一样, 他就是 Linux 的根文件夹 (/) 的基础组成.
| | |
bin
boot
dev
etc
home
lib
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
其他的都可以不管, 只把编译出来的 U-boot, DTB, Kernel 一起放进 RootFS 里, 他就是一个完整的 Arch Linux 了. 所以, 我们只需要下载硬件厂商提供的整套编译工具, 然后编译这三部分, 放进 RootFS 里就可以得到一个新的 Arch Linux 了. 当然, 对于我这样的 Linux 萌新, 肯定是要借助亿点点 AI 神力了.
这部分依硬件厂商而定, 我就不演示了. 大致的流程可以总结一下, 下载硬件厂商提供的源码, 然后编译内核, 设备树, U-boot; 然后去 archlinuxarm.org 下载并解压 RootFS; 把新的内核, 设备树, U-boot 放进去 (需要注意 U-boot 起始字节偏移量), 然后重新打包就好了. 如果还想要深度定制的话, 可以先安装 qemu-user-static 和 qemu-user-static-binfmt, 这个可以直接在 X86 电脑上直接跨架构透明运行 ARM 的软件; 然后使用 arch-chroot 挂载这个文件为根文件, 在这个新的根文件里就可以随意的定制了.
我的这块板子绝大多数驱动直接在内核源码里就有的, 所以也没有花太大的力气 (也许). patch 了两个地方就可以正常运行了, 一个是主板供电, 一个是 TF (MicroSD) 卡插槽 (突然想起来, 好像应该用 f2fs 文件系统的). 唯一比较麻烦的是无线网卡的驱动, 八万行 C, 要整体迁移过去感觉不太可能了, 直接放弃了. (毕竟无线网卡也是开源社区公认的难以逾越的史山, 要给他写驱动很多都需要直接逆向)

第一次看到那行蓝色的 "Wolcomse to Arch Linux" 这么兴奋
后续维护
我的这块开发板默认的 Linux 内核还停留在上古时期的 5.16.17, 老就算了, 还不是 TLS 版本, 没有官方的安全更新, 估计可以花式解锁 ROOT 权限. 这样的内核肯定是没办法安全使用的.
前面制作镜像的方式会直接把内核固定在镜像里, 这样的话每次更新内核版本都需要重新烧录一次镜像, 相当麻烦. 可以写一份简单的 PKGBUILD 把他打包成 pacman 软件包.

说起来, 为什么每次提到系统的时候都要展示一下自己的 fastfetch? 像极了某种神秘的仪式, 不是很懂你们用 Linux 的.

不过嘛, 就像是前面提到的, 因为这块板子的性能太孱弱了, 基本上干啥啥不行. 2GB 的内存, 基本上也跑不了 CI; 21MiB/s 的顺序写 + 465IOPS 的 4k 随机写入, 连数据库都几乎跑不了.

所以, 我基本上也是只拿这个东西来下载动画片看, 用 podman 部署 qbittorrent-nox + jellyfin, 就可以在局域网建一个简单的动画片专属 NAS. (所以折腾了好几天的意义在哪里)
这是我的配置 (注意不要部署到公网, 真的要部署的话至少加上 mTLS):
# compose.yml
services:
qbittorrent-nox:
container_name: qbittorrent-nox
environment:
QBT_LEGAL_NOTICE: "confirm"
QBT_TORRENTING_PORT: "6881"
QBT_WEBUI_PORT: "8080"
QBT_CONFIG_PATH: "./config"
QBT_DOWNLOADS_PATH: "./downloads"
JELLYFIN_CONFIG_PATH: "./jellyfin_config"
JELLYFIN_CACHE_PATH: "./jellyfin_cache"
image: qbittorrentofficial/qbittorrent-nox:latest
ports:
- "6881:6881/tcp"
- "6881:6881/udp"
- "8080:8080/tcp"
read_only: true
stop_grace_period: 30s
tmpfs:
- /tmp
tty: true
volumes:
- "./config:/config"
- "./downloads:/downloads"
restart: unless-stopped
jellyfin:
image: jellyfin/jellyfin
container_name: jellyfin
environment:
QBT_LEGAL_NOTICE: "confirm"
QBT_TORRENTING_PORT: "6881"
QBT_WEBUI_PORT: "8080"
QBT_CONFIG_PATH: "./config"
QBT_DOWNLOADS_PATH: "./downloads"
JELLYFIN_CONFIG_PATH: "./jellyfin_config"
JELLYFIN_CACHE_PATH: "./jellyfin_cache"
ports:
- 8096:8096/tcp
- 7359:7359/udp
volumes:
- "./jellyfin_config:/config"
- "./jellyfin_cache:/cache"
- type: bind
source: ./downloads
target: /media
read_only: true
restart: unless-stopped
media-nfs:
image: ghcr.io/obeone/nfs-server:latest
container_name: qbit-media-nfs
environment:
NFS_VERSION: "4.2"
NFS_DISABLE_VERSION_3: "1"
NFS_EXPORT_0: >-
/exports ${NFS_ALLOWED_SUBNET:-192.168.1.0/24}(ro,fsid=0,sync,no_subtree_check,insecure,root_squash)
ports:
- "2049:2049/tcp"
cap_add:
- SYS_ADMIN
- SYS_MODULE
volumes:
- type: bind
source: ./downloads
target: /exports
read_only: true
- type: bind
source: /lib/modules
target: /lib/modules
read_only: true
restart: unless-stopped
media-http:
image: docker.io/library/nginx:alpine
container_name: qbit-media-http
ports:
- "8097:80/tcp"
volumes:
- type: bind
source: ./downloads
target: /usr/share/nginx/html
read_only: true
- type: bind
source: ./nginx.conf
target: /etc/nginx/conf.d/default.conf
read_only: true
restart: unless-stopped
# nginx.conf
他支持使用 nfs 挂载到本地, 也支持直接 mpv <url> 本地播放, 感觉 jellyfin 很多余, 还浪费 0.5G 内存.

好久没有玩视觉小说了, 本来想玩玩新出的 《とける風花とシロうさぎ》, 但是他在 Win10 虚拟机里因为缺少组件跑不起来, 但是 proton 却可以, 很奇怪.
并且中文补丁还有字体回退问题, 字体一个大一个小的, 鼓捣了一个晚上, 用一个魔改的假 思源字体 (字体标识是日文的 "源ノ角ゴシック", 但是里面装的是中文的 "思源黑体"), 解决了. (好麻烦, 脑子不够用了)


-
在开源社区普遍 AMD64 叫的比较多, 因为 AMD 才是该规范的提出者, 并在 2003 年推出了首款 AMD64 架构的 64 位 X86 处理器 -- Opteron. 英特尔当时使用的是 IA-64 架构, 虽然也是 64 位, 但是并不兼容 32 位 X86. 在市场接受度上 Intel 败局已定, 被迫找 AMD 买下了 AMD64 架构的授权. ↩