Arch Linux 上的用户态 Linux (Usermode Linux) 调教指南

Linux

其实去年的这个时候就在玩儿这个了,那时候 BBR 还在主线,想 用 UML 来充分利用一下 OpenVZ VPS据说 早期的 Linode 就是用这个方法搞虚拟化卖 VPS 的。

然而就像 ArchWiki 的警告栏中描述的一样,这是个 Dead project, last activity in 2007。尽管如此,我们还是可以基于最新的 Linux 源代码运行它。

编译

AUR 里的 linux-usermode 包的 PKGBUILD 有点年久失修,本来就应该像一般的内核编译过程一样,只不过加上 ARCH=um 表示需要编译用户态内核,几行命令就够了。

export ARCH=um
make defconfig
make prepare
make vmlinux modules

但是由于 glibc 的问题,在 Arch Linux 上直接这样编译会报下面这样的错误。

arch/um/os-Linux/signal.c: In function ‘hard_handler’:
arch/um/os-Linux/signal.c:163:22: error: dereferencing pointer to incomplete type ‘struct ucontext’
  mcontext_t *mc = &uc->uc_mcontext;
                      ^~
make[1]: *** [scripts/Makefile.build:315: arch/um/os-Linux/signal.o] Error 1
make: *** [Makefile:1025: arch/um/os-Linux] Error 2

解决方法参考 这里 ,把 patch 打上就好啦。

当然你还可以试试我写的 PKGBUILD ,默认会加上 bbr 因为这才是最开始的目的 ,不想这样改的话清空掉 extra_config 就好啦。

按上面的方法编译完成的话,你会得到这两个东西。

一个是内核 vmlinux ,如果用 PKGBUILD 的话会生成一个叫 linux-usermode 的包。

另一个是可用的内核模块 modules ,对应的包是 linux-usermode-modules

我们通常把内核和内核模块一起打包,但是现在我们必须要分开它们,因为它们会被 安装在不同的地方

配置

安装

其实把编译好的 vmlinux 复制到宿主机上就好啦。如果你像我一样觉得还是用包管理器更清真一些,那只需要在宿主机上安装 linux-usermode 这个包。 module 不需要安装到宿主机的文件系统。

配置 rootfs

首先得有个镜像文件作为 rootfs ,不想太占硬盘又怕分得太小不够装的话就用稀疏文件吧。 ddtruncate 都是很棒的工具。

$ dd of=rootfs.img bs=1M seek=20480 count=0
记录了0+0 的读入
记录了0+0 的写出
0 bytes copied, 1.8298e-05 s, 0.0 kB/s

### 或者使用 truncate
$ truncate -s 20G rootfs.img

使用 mkfs 在这块空白的镜像文件上创建你喜欢的操作系统,一般来说用 ext4 就好啦。

然后就是挂载这个镜像并装入系统啦,装系统什么的自己围观 ArchWiki 就好。其它发行版应该也都没问题。当然内核和配置启动就不需要了。

几件小事

首先 arch-chroot 进到你的 rootfs 里面。

修改启动时的 tty

当然你也可以在启动后用 screen 连上去。

systemctl enable getty@tty0.service
systemctl disable getty@tty1.service

重新挂载 rootfs 为可写

用户态内核会将 rootfs 挂为只读,但是我们可以加个 systemd-unit 重新挂载一下

[Unit]
Description=Remount rootfs as rw

[Service]
Type=oneshot
ExecStart=/usr/bin/mount -o remount,rw /

[Install]
WantedBy=getty.target

然后 enable 它就好。

haveged

看起来随机数生成器并不能正常工作,所以还是装上这个然后 systemctl enable haveged

安装模块

实际上 linux-usermode-modules 这个包是装在 rootfs 里而不是宿主机上的,这样 modprobe 才能找到内核模块并加载它们。这也就是我们必须把用户态内核与它的模块分开打包的原因。 其实没有内核模块不也一样跑。

网络

参考 Gentoo Wiki

其实就是简单地用 iptables 做个 NAT 。

sudo modprobe tun
sudo modprobe iptable_nat
sudo systemctl start iptables
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

记得把 eth0 换成你自己上网用的那个端口。

然后给 UML 划个 IP 段,比如我用的是 192.168.210.0/24。然后宿主机这边的 IP 用 192.168.210.1, UML 那边用 192.168.210.101

然后 arch-chroot 到 UML 的 rootfs 里加上网络配置,直接写静态 IP 就好。我习惯用 netctl ,当然 systemd-network 应该都能工作。

Description='Network for Usermode Linux with NAT'
Interface=eth0
Connection=ethernet
IP=static
Address=('192.168.210.101/24')
Gateway='192.168.210.1'
DNS=('192.168.1.1')

走之前记得顺手 enable 了它。

启动

启动自然就是一条命令的事情。

vmlinux \
    ubd0=rootfs.img \
    eth0=tuntap,,,192.168.210.1 \
    mem=512M

配置的写法基本上就是 设备名=参数 的形式, eth0 后面接的是宿主机上打算给 NAT 用的网关 IP 。启动后会在宿主机上自动产生一个 tap0 接口并添加路由表。