---
title: 给 OrangePi 3 LTS 装上 Arch
tags:
  - 折腾
  - linux
cover-image: https://img.gsgfs.moe/images/raw/9a/64/9a64b37bc1c952e281525c1007354cca13d9d5590a2db008d69f4e1615cbed10.png
slug: Install-Arch-on-OrangePi-3-LTS
category:
  - 折腾
---

# 给 OrangePi 3 LTS 装上 Arch

前几天(?), 整理东西的时候, 新鲜出土了一个上古时代的产物, 一块 Linux 开发板, 没错, 类似于 Raspberry Pi 一样的东西, 一般也被称为单板电脑 -- 一块开发板就是一个完整的电脑, ~~除了没有外设~~

![开发板本体](https://img.gsgfs.moe/images/raw/20/d8/20d8b10a61a2ad5d587a66240edee0ba79d4dd6175a109ea8b7d1dbc998866d1.jpg)

相应的, 在科技圈, 便携往往也是性能孱弱的代名词. 像我手上这块, 使用的是四核 Cortex-A53. 这大概是个什么水平呢? ~~可能还没有你家的路由器性能高~~. 使用 [sysbench](https://github.com/akopytov/sysbench)
跑分, 单核只有可怜的 900 分. (而承载这个网站运转的那台服务器, 单核跑分有 5300. ~~甚至比我自己的电脑还高~~)

因此, 并不推荐你去买这个, 并且社区资源太少了, 正的又需要的话可以去看看 [Raspberry Pi](https://www.raspberrypi.com/) 至少他有一个强大的社区, 可以提供相当多的学习资源. (~~从板子上的灰也多少可以看出一点端倪~~)

![正在编译内核](https://img.gsgfs.moe/images/raw/cd/d8/cdd88a0a76047810003846541a337de81d87924a3e66662767766a70a7a32629.jpg)

## 原理

首先, 这个东西的运行原理跟你常用的 [X86_64](https://zh.wikipedia.org/wiki/X86-64) (也称 AMD64, Intel 管他叫 Intel 64/EM64T)[^1] 架构的电脑还是有很大的区别的, 因为指令集授权问题, 实际上没有几家公司可以生产 X86_64 架构的处理器. 基本上一只手都可以数的过来, 1. AMD; 2. Intel; 3. 上海兆芯; 4. 海光信息 (~~并且后两者生产的 CPU 在性能上几乎不可用~~). 再加上 [CISC 架构](https://en.wikipedia.org/wiki/Complex_instruction_set_computer) 的能效问题, 这个领域基本上都是 [ARM 架构](https://zh.wikipedia.org/wiki/ARM%E6%9E%B6%E6%A7%8B) 的天下. (~~当然, 还有 RISC-V, 但是他太小众了~~)

[^1]: 在开源社区普遍 AMD64 叫的比较多, 因为 AMD 才是该规范的提出者, 并在 2003 年推出了首款 AMD64 架构的 64 位 X86 处理器 -- Opteron. 英特尔当时使用的是 IA-64 架构, 虽然也是 64 位, 但是并不兼容 32 位 X86. 在市场接受度上 Intel 败局已定, 被迫找 AMD 买下了 AMD64 架构的授权.

但是呢, 因为 ARM 除了服务器领域和 Windows on ARM, 基本上都不用那个在 X86_64 上相当好的 UEFI, 大概主要还是因为他的集成度过高 (毕竟芯片都叫做 [SoC](https://zh.wikipedia.org/wiki/%E5%8D%95%E7%89%87%E7%B3%BB%E7%BB%9F) 了), 没有统一的总线协议, 做不到 "硬件自发现". 毕竟芯片厂直接对接手机厂, 谁也不想做通用兼容, ~~这群人太坏了!~~.

那么 ARM 架构的 Linux 是如何启动的呢? 一般来说, 这类硬件在上电之后会固定运行烧录在 ROM 上的固定的启动代码, 在存储介质中的固定位置加载 Bootloader; 然后 Bootloader 接手启动流程, 加载 Linux 内核和 DTB (设备树, 负责直接告诉内核, 这个硬件上有哪些设备); 然后启动 Linux 内核并转交控制权; 然后 Linux 内核挂载根文件系统, 启动第一个用户态程序 (一般来说是 `/sbin/init`, 如果使用 systemd 的话, 通常是 systemd 会接管); 然后从这个用户态程序拉起整个用户态空间 (网络, 图形界面等).

![吓哭了](https://img.gsgfs.moe/images/raw/71/92/71923e0349d9751f134a0ca6ba61a342f952b2756a9b6d2ef836c564f5c52597.jpg)

如果里手里有 Linux 设备的话, 也可以直接查一下, 是谁在接管 `/sbin/init`.

:::terminal{title="查看 /sbin/init" shell="zsh" prompt="❯"}

```zsh
file /sbin/init
```

```output
/sbin/init: symbolic link to ../lib/systemd/systemd
```

:::

那么, 综上所述, 一个可供开发板刷写的镜像主要由四部分组成, 启动方式 (一般来说是 U-boot), Linux 内核, 设备树文件, 一组基础软件包.

## 制作镜像文件

那么我们要怎么找这四个组成部分呢? 一般来说, 硬件厂商会直接提供所有需要的组成部分. 比如说我这块开发板就提供了基于 Ubuntu 制作的镜像, 和完整的镜像制作套件.

首先, 我们回顾一下, Linux 发行版的边界在哪? Arch Linux 有一个很火的衍生版, [CachyOS](https://cachyos.org/). 他四舍五入跟 Arch Linux 没有什么区别, 使用同样的包管理器, 系统组成也几乎一模一样, 甚至, 海岸可以直接在 Arch Linux 上加入 CachyOS 的[软件源](https://wiki.cachyos.org/features/optimized_repos/#adding-our-repositories-to-an-existing-arch-linux-install), 变成 CachyOS 风味 Arch.

但是, 怎么做之后, 所有的软件包都变成由 CachyOS 提供的, 那么你的这条 [忒修斯之船](https://en.wikipedia.org/wiki/Ship_of_Theseus) 还算是 Arch 吗? (甚至, 你还可以安装 `cachyos-settings` 和 `cachyos-hooks` 这两个包, 把他完全 Cachy 化)

(我之前也加入了 CachyOS 的软件源体验了一下, 对于我的电脑, 他大概有 5% 左右的性能提升. 但是, 他有一个巨大的 "缺陷", 对于一些更新频繁的软件, CachyOS 的软件源永远落后一到两个版本, ~~用不到最新的软件, 你知道这对一个 Arch 用户来说有多大的心理伤害嘛~~)

![arch meme](https://img.gsgfs.moe/images/raw/68/12/6812fb014b7ab738d860e7d410ebaa9f31ba67c89b1b52a3bd5db71c3716b026.jpg)

好像扯远了, 总之, 把系统的软件包管理器和软件包换成 Arch 的 pacman, 他就是 Arch Linux 了.

对应到上面说的, 镜像的四个组成部分, 就是最后那个 "一组基础软件包" 了. 那么要如何替换呢? 这些基础的预装组件一般都是 RootFS 提供的, 跟他的名字一样, 他就是 Linux 的根文件夹 (`/`) 的基础组成.

:::terminal{title="查看RootFS的内容" shell="zsh" prompt="❯"}

```zsh
bsdtar -tf Arch LinuxARM-aarch64-latest.tar.gz | sed 's|^\./||' | awk -F'/' '{print $1}' | sort -u
```

```output
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; 然后去 <https://archlinuxarm.org> 下载并解压 RootFS; 把新的内核, 设备树, U-boot 放进去 (需要注意 U-boot 起始字节偏移量), 然后重新打包就好了. 如果还想要深度定制的话, 可以先安装 `qemu-user-static` 和 `qemu-user-static-binfmt`, 这个可以直接在 X86 电脑上直接跨架构透明运行 ARM 的软件; 然后使用 arch-chroot 挂载这个文件为根文件, 在这个新的根文件里就可以随意的定制了.

我的这块板子绝大多数驱动直接在内核源码里就有的, 所以也没有花太大的力气 (~~也许~~). patch 了两个地方就可以正常运行了, 一个是主板供电, 一个是 TF (MicroSD) 卡插槽 (突然想起来, 好像应该用 [f2fs](https://en.wikipedia.org/wiki/F2FS) 文件系统的). 唯一比较麻烦的是无线网卡的驱动, 八万行 C, 要整体迁移过去感觉不太可能了, 直接放弃了. (~~毕竟无线网卡也是开源社区公认的难以逾越的史山, 要给他写驱动很多都需要直接逆向~~)

![C 语言 meme](https://img.gsgfs.moe/images/raw/ec/a6/eca6e53fe44ab36d563ae99e3beb84aa0584b662536a1fe70fde5d9435fdf957.jpg)

~~第一次看到那行蓝色的 "Wolcomse to Arch Linux" 这么兴奋~~

## 后续维护

我的这块开发板默认的 Linux 内核还停留在上古时期的 5.16.17, 老就算了, 还不是 TLS 版本, 没有官方的安全更新, ~~估计可以花式解锁 ROOT 权限~~. 这样的内核肯定是没办法安全使用的.

前面制作镜像的方式会直接把内核固定在镜像里, 这样的话每次更新内核版本都需要重新烧录一次镜像, 相当麻烦. 可以写一份简单的 `PKGBUILD` 把他打包成 pacman 软件包.

![fastfetch 时间](https://img.gsgfs.moe/images/raw/f4/92/f492e4ffd7048b10cae8a86826b4965ba4dcf6e1735674f59b63c6e5cfc028e4.png)

说起来, 为什么每次提到系统的时候都要展示一下自己的 `fastfetch`? ~~像极了某种神秘的仪式~~, 不是很懂你们用 Linux 的.

![Linux User](https://img.gsgfs.moe/images/raw/40/d1/40d16ed8d67c9b9cc6a04a1df586df0d7e085e10ad5a79eeda9f03bc85b06447.jpg)

不过嘛, 就像是前面提到的, 因为这块板子的性能太孱弱了, 基本上干啥啥不行. 2GB 的内存, 基本上也跑不了 CI; 21MiB/s 的顺序写 + 465IOPS 的 4k 随机写入, 连数据库都几乎跑不了.

![SQLite meme](https://img.gsgfs.moe/images/raw/f9/7b/f97b7cf68b18f8ed03893c21245f81c4096dd2ba90ff9fd4d5dd0ac8ce0c219e.jpg)

所以, 我基本上也是只拿这个东西来下载动画片看, 用 podman 部署 [qbittorrent-nox](https://github.com/qbittorrent/docker-qbittorrent-nox) + [jellyfin](https://github.com/jellyfin/jellyfin), 就可以在局域网建一个简单的动画片专属 NAS. (~~所以折腾了好几天的意义在哪里~~)

这是我的配置 (注意不要部署到公网, 真的要部署的话至少加上 mTLS):

```yaml
# 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
# nginx.conf
server {
    listen 80;
    server_name _;

    root /usr/share/nginx/html;
    charset utf-8;

    location / {
        autoindex on;
        autoindex_exact_size off;
        autoindex_localtime on;
    }
}
```

他支持使用 [nfs](https://wiki.archlinux.org/title/NFS) 挂载到本地, 也支持直接 `mpv <url>` 本地播放, ~~感觉 jellyfin 很多余, 还浪费 0.5G 内存~~.

![使用mpv播放视频](https://img.gsgfs.moe/images/raw/34/16/341646b7d398f7e260e6ac3648f36c5955168510ee5e57384c3d0bdd3d418419.png)

---

好久没有玩视觉小说了, 本来想玩玩新出的 《とける風花とシロうさぎ》, 但是他在 Win10 虚拟机里因为缺少组件跑不起来, 但是 proton 却可以, 很奇怪.

并且中文补丁还有字体回退问题, 字体一个大一个小的, 鼓捣了一个晚上, 用一个魔改的假 [思源字体](https://github.com/adobe-fonts/source-han-sans) (字体标识是日文的 "源ノ角ゴシック", 但是里面装的是中文的 "思源黑体"), 解决了. (~~好麻烦, 脑子不够用了~~)

![等等, 你是...](https://img.gsgfs.moe/images/raw/9a/64/9a64b37bc1c952e281525c1007354cca13d9d5590a2db008d69f4e1615cbed10.png)

---

![高性能](https://img.gsgfs.moe/images/raw/3d/05/3d056970e8debb7e276912c4b1738eae9fd6d82f46fe284c94ecd3ba81790293.jpg)