最近在折腾信息学竞赛环境时,用到了官方的 NOI Linux 2.0 镜像。官方系统底层基于 Ubuntu 20.04.1 Focal,虽然竞赛工具一应俱全,但日常拿来训练和使用时,有些地方确实让人抓狂。为此,我花时间基于 Windows + WSL2 深度定制了一份自用版镜像,顺便把全流程和踩坑经验整理记录下来。


为什么折腾定制?

官方提供的 ubuntu-noi-v2.0.iso 作为比赛和训练环境非常稳定,但有几个极其影响日常使用体验的痛点:

  1. 自带浏览器太老古董:自带的 Firefox 79.0 连主流扩展都装不上,比如 Bitwarden 插件最低要求 Firefox 91+,直接提示不兼容。
  2. 缺乏日常交流工具:训练过程中少不了交流传题,没有官方 Linux QQ 确实不太方便。
  3. 网络分流需求:很多算法资源和依赖都在外网,为了下载不卡壳,需要一套类似 v2rayN 的网络代理与国内外流量分流方案(通过 v2rayA + Xray 实现)。
  4. 铁律:绝不能破坏竞赛基准:这是重中之重!Kernel 5.4.0-42、GCC/G++ 9.3.0、Python 2.7/3.8、GDB 9.1、Code::Blocks 20.03 等所有评测相关工具版本必须完全保持原样,不能有丝毫偏差。

先说几个踩过的大坑(敲黑板)

在动手前,强烈建议先看这几个避坑点,能省下几个小时排错时间:

  • 解包 SquashFS 目录冲突: unsquashfs -d squashfs-root ... 默认要求目标文件夹不能预先存在。如果你手快提前 mkdir squashfs-root,它会直接报错 FATAL ERROR: dir_scan: failed to make directory squashfs-root, because File exists 并中断。
  • 千万不要手痒全局 apt upgrade: Ubuntu 的内核升级往往以新包(例如 linux-image-5.4.0-xxx-generic)形式出现,普通的 apt-mark hold 很可能会被新包名绕过,导致内核直接变掉。所以原则是:坚决不执行全局升级,只对需要的软件做定点安装。
  • Firefox 平滑替换为 ESR: 直接添加 Mozilla 官方源,把原版替换为长期支持的 firefox-esr,并设置软链接让系统默认的 /usr/local/bin/firefox 直接指向 ESR 版,既不影响脚本调用,又能痛快装插件。
  • v2rayA 2.4.x 的版本冲突坑: v2rayA 最新的 2.4.x 安装后容易报 core version mismatch: v2raya_core version does not match v2rayA version 错误。实测最稳妥的方案是:直接安装 GitHub 官方编译的 Xray 二进制包,并搭配锁死版本的 v2rayA 2.2.7.5 deb 包,底层无缝支持国内直连与大陆白名单分流。
  • 原版 ISO 的引导兼容性: 原版 NOI Linux 2.0 采用的是 Legacy BIOS / isolinux 引导结构,重新打包 ISO 时一定要指定好 isolinux/boot.cat 与 isolinux/isolinux.bin 参数,否则压出来的镜像会无法引导启动。

动手操作全流程

这里我们直接在 Windows 的 WSL2(推荐 Ubuntu 22.04)中操作。假设原版镜像 ubuntu-noi-v2.0.iso 和下载好的官方 QQ deb 安装包都放在 Windows 的 E:\ 根目录下。

1. 准备工作与安装工具

打开 WSL2 终端,创建工作目录并拷贝文件(拷贝到 Linux 本地文件系统操作速度更快):

1
2
3
4
5
6
7
8
9
10
11
12
# 清理并创建工作目录(注意:千万不要提前建 squashfs-root 目录)
rm -rf ~/noi-custom
mkdir -p ~/noi-custom/{iso-root,packages}
cd ~/noi-custom

# 将 E 盘的文件复制到当前工作目录
cp /mnt/e/ubuntu-noi-v2.0.iso ./ubuntu-noi-v2.0.iso
cp /mnt/e/*.deb ./packages/

# 安装打包所需的工具链
sudo apt update
sudo apt install -y squashfs-tools xorriso rsync wget ca-certificates gnupg

2. 提取 ISO 与解包根文件系统

1
2
3
4
5
# 提取 ISO 中的所有文件到 iso-root
xorriso -osirrox on -indev ubuntu-noi-v2.0.iso -extract / iso-root

# 解压系统的只读根文件系统 (unsquashfs 会自动创建 squashfs-root)
sudo unsquashfs -d squashfs-root iso-root/casper/filesystem.squashfs

3. 挂载并进入 Chroot 环境

把安装包和 DNS 配置文件送进目标系统,挂载虚拟文件系统后切进去:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 拷贝待安装的 deb 包和 DNS 解析配置
sudo cp -r packages squashfs-root/tmp/packages
sudo cp /etc/resolv.conf squashfs-root/etc/resolv.conf

# 绑定挂载虚拟文件系统
sudo mount --bind /dev squashfs-root/dev
sudo mount --bind /dev/pts squashfs-root/dev/pts
sudo mount --bind /proc squashfs-root/proc
sudo mount --bind /sys squashfs-root/sys
sudo mount --bind /run squashfs-root/run

# 正式进入 chroot 隔离环境
sudo chroot squashfs-root /bin/bash

4. 系统内部定制(在 Chroot 终端中操作)

此时我们已经在目标系统的根目录下了,可以开始做各种定制。

特别提醒:在后续执行任意 apt install 或 apt remove 过程中,终端如果弹出一串 ERROR couldn't connect to zsys daemon... /run/zsysd.sock 的红色报错,不必惊慌。这是 Ubuntu 的 ZFS 自动快照服务在 chroot 隔离环境下找不到后台守护进程引发的钩子报错,属于非致命提示,完全不影响软件包的安装与卸载,直接忽略即可。

4.1 锁定核心竞赛环境

为了防止后续操作误伤比赛必备环境,我们先给官方工具包打上保护锁:

1
2
3
4
5
6
7
8
9
10
11
12
13
apt update

# 锁定内核与竞赛工具链版本
apt-mark hold \
linux-generic linux-image-generic linux-headers-generic \
linux-image-5.4.0-42-generic linux-modules-5.4.0-42-generic linux-modules-extra-5.4.0-42-generic \
gcc gcc-9 g++ g++-9 \
fpc fp-compiler fp-compiler-3.0.4 \
python2.7 python3.8 \
gdb ddd \
codeblocks lazarus geany \
code emacs gedit vim joe nano sublime-text \
mc xterm

4.2 换装 Firefox ESR

添加 Mozilla 官方源并换上 ESR 长期支持版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
install -d -m 0755 /etc/apt/keyrings

# 导入 Mozilla 官方 GPG 密钥
wget -q https://packages.mozilla.org/apt/repo-signing-key.gpg -O- \
| tee /etc/apt/keyrings/packages.mozilla.org.asc >/dev/null

# 添加软件源
cat > /etc/apt/sources.list.d/mozilla.list <<'EOF'
deb [signed-by=/etc/apt/keyrings/packages.mozilla.org.asc] https://packages.mozilla.org/apt mozilla main
EOF

# 设置源优先级,优先使用官方源
cat > /etc/apt/preferences.d/mozilla <<'EOF'
Package: *
Pin: origin packages.mozilla.org
Pin-Priority: 1000
EOF

# 卸载老旧版本,安装中文版 Firefox ESR
apt update
apt remove -y firefox || true
apt install -y firefox-esr firefox-esr-l10n-zh-cn

# 建立软链接,保证全局命令直接调用 ESR
ln -sf /usr/bin/firefox-esr /usr/local/bin/firefox

4.3 安装 Linux QQ

1
2
3
4
5
# 先模拟检查是否有依赖冲突
apt install -s /tmp/packages/*.deb

# 确认无冲突后正式安装
apt install -y /tmp/packages/*.deb

4.4 安装配置 v2rayA 与 Xray

为了解决 2.4.x 的核心校验异常,这里直接部署官方编译的 Xray 独立二进制,并搭配稳定性极佳的 v2rayA 2.2.7.5 版本并锁定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 1. 安装解压所需工具
apt install -y unzip

# 2. 下载并安装官方最新版 Xray 二进制(若下载慢,也可在 Windows 下好放到 packages 目录里拷贝过来)
wget -O /tmp/xray.zip https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-64.zip

unzip -o /tmp/xray.zip -d /tmp/xray-bin
install -m 755 /tmp/xray-bin/xray /usr/local/bin/xray
mkdir -p /usr/local/share/xray
cp -f /tmp/xray-bin/geoip.dat /usr/local/share/xray/
cp -f /tmp/xray-bin/geosite.dat /usr/local/share/xray/
rm -rf /tmp/xray*

# 验证 xray 是否可用
xray version

# 3. 下载并安装兼容的 v2rayA 2.2.7.5 版本
wget -O /tmp/v2raya_2.2.7.5.deb \
https://github.com/v2rayA/v2rayA/releases/download/v2.2.7.5/installer_debian_x64_2.2.7.5.deb

apt install -y --allow-downgrades /tmp/v2raya_2.2.7.5.deb
apt-mark hold v2raya

# 4. 设置开机自启动
systemctl enable v2raya.service || true

4.5 清理缓存并退出 Chroot

系统定制完成后,把临时包和缓存清理干净再退出:

1
2
3
4
apt clean
rm -rf /var/lib/apt/lists/*
rm -rf /tmp/packages /tmp/v2raya_2.2.7.5.deb /tmp/xray*
exit

5. 卸载挂载点与重新生成 SquashFS

回到宿主机的终端,开始清理挂载并压缩系统文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 卸载所有绑定的虚拟目录
sudo umount -l squashfs-root/run
sudo umount -l squashfs-root/sys
sudo umount -l squashfs-root/proc
sudo umount -l squashfs-root/dev/pts
sudo umount -l squashfs-root/dev

# 重新生成系统已安装软件包的 manifest 清单
sudo chroot squashfs-root dpkg-query -W --showformat='${Package} ${Version}\n' \
| sudo tee iso-root/casper/filesystem.manifest >/dev/null
sudo cp iso-root/casper/filesystem.manifest iso-root/casper/filesystem.manifest-desktop

# 压制新的 SquashFS 文件(使用 xz 高压缩比,会多花几分钟)
sudo rm -f iso-root/casper/filesystem.squashfs
sudo mksquashfs squashfs-root iso-root/casper/filesystem.squashfs -comp xz

# 更新镜像大小信息
printf "$(sudo du -sx --block-size=1 squashfs-root | cut -f1)" \
| sudo tee iso-root/casper/filesystem.size

6. 重算校验和并打包封装 ISO

最后一步,更新哈希记录并封装成可引导的 ISO 镜像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cd ~/noi-custom/iso-root

# 重新生成 md5sum.txt 校验文件
sudo rm -f md5sum.txt
sudo bash -c 'find . -type f \
! -path "./isolinux/boot.cat" \
! -path "./isolinux/isolinux.bin" \
! -path "./md5sum.txt" \
-print0 | xargs -0 md5sum > md5sum.txt'

# 按照 isolinux 引导参数生成 ISO
sudo xorriso -as mkisofs \
-r -J -l -iso-level 3 \
-V "ISOIMAGE" \
-o ../ubuntu-noi-v2.0-custom.iso \
-c isolinux/boot.cat \
-b isolinux/isolinux.bin \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
.

# 将制作好的新镜像复制回 Windows E 盘根目录
cp ~/noi-custom/ubuntu-noi-v2.0-custom.iso /mnt/e/ubuntu-noi-v2.0-custom.iso

验证与效果

镜像制作完成后,直接在 VMware 或 VirtualBox 中挂载 E:\ubuntu-noi-v2.0-custom.iso 启动测试:

  1. 核心版本验证: 打开终端依次运行以下命令,确认竞赛环境原封未动:
    1
    2
    3
    4
    gcc --version       # 依然是 9.3.0
    g++ --version # 依然是 9.3.0
    python3 --version # 依然是 3.8.x
    gdb --version # 依然是 9.1
  2. 现代浏览器体验: 打开 Firefox,已经是现代的 Firefox ESR 版本,去扩展商店安装 Bitwarden 等插件一路顺畅。
  3. QQ 与网络分流: QQ 正常登录使用;浏览器打开 http://127.0.0.1:2017 进入 v2rayA Web 界面,配置好节点并开启“大陆白名单”或“RoutingA 国内直连”,国内网站直连、外网顺畅分流。

至此,一个既保留了原版绝对纯正的 NOI 竞赛评测基准,又兼顾了日常使用便利的定制版 NOI Linux 2.0 就大功告成了!平时训练用起来舒服多了~