xen 相关知识
xen 主要由管理程序、主机操作系统(Dom0)、客户机操作系统(DomU) 组成。
管理程序用来生成、删除、管理虚拟机的 cpu 或内存等资源信息。主机操作系统具有向管理程序请求虚拟机环境控制处理的权限。主机操作系统中存在叫做 xend 的守护进程或者设备仿真器 qemu-dm 进程。
xend 是接受生成客户端操作系统请求的守护进程,从 xen 命令处接受处理要求,基金共有主机操作系统向管理程序请求生成虚拟机等。
qemu-dm 也称为设备模型,它是用来仿真虚拟机设备的进程,用来仿真 VGA 或 IDE 等设备。
安装 xen
执行如下命令安装 xen:
sudo apt-get install xen-hypervisor-4.11-amd64
安装过程输出的 log 中有与 grub 相关的信息,记录如下:
Including Xen overrides from /etc/default/grub.d/xen.cfg
WARNING: GRUB_DEFAULT changed to boot into Xen by default!
Edit /etc/default/grub.d/xen.cfg to avoid this warning.
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.19.0-8-amd64
Found initrd image: /boot/initrd.img-4.19.0-8-amd64
Found linux image: /boot/vmlinuz-4.19.0-8-amd64
Found initrd image: /boot/initrd.img-4.19.0-8-amd64
Found linux image: /boot/vmlinuz-4.19.0-8-amd64
Found initrd image: /boot/initrd.img-4.19.0-8-amd64
Found linux image: /boot/vmlinuz-4.19.0-8-amd64
Found initrd image: /boot/initrd.img-4.19.0-8-amd64
done
安装成功后查看 /boot 目录,发现有添加新的文件。/boot 目录中文件内容列表如下:
longyu@virt-debian10:~$ ls /boot
config-4.19.0-8-amd64 initrd.img-4.19.0-8-amd64 System.map-4.19.0-8-amd64 xen-4.11-amd64.config xen-4.11-amd64.gz
grub initrd.img-4.19.0-8-amd64-changed vmlinuz-4.19.0-8-amd64 xen-4.11-amd64.efi
xen-xx 的文件是多出来的,这是我上一步执行的安装命令添加的。
运行 xen
执行 xen 命令有如下报警:
Can't find hypervisor information in sysfs
上述信息表明我当前系统不支持 hypervisor,上面我只是安装了 xen,要生效需要重启,并选择 xen 的引导项目。
进入系统后重新执行 xen 命令,有如下输出信息:
longyu@virt-debian10:~$ sudo xen list
Name ID Mem VCPUs State Time(s)
Domain-0 0 1893 7 r----- 11.4
Dom0 就是 xen 的主机操作系统,上述输出表明 xen 工作正常,主机操作系统正常运行。我执行 ps 命令查找与 xen 相关的进程,获取到了如下信息:
longyu@virt-debian10:~$ ps aux | grep xen
root 48 0.0 0.0 0 0 ? S 20:58 0:00 [xenbus]
root 49 0.0 0.0 0 0 ? S 20:58 0:00 [xenwatch]
root 588 0.0 0.2 15928 3800 ? S 20:58 0:00 /usr/lib/xen-4.11/bin/oxenstored --pid-file /var/run/xenstore.pid
root 594 0.0 0.0 68512 1660 ? Sl 20:58 0:00 /usr/lib/xen-4.11/bin/xenconsoled --pid-file /var/run/xenconsoled.pid
root 601 0.0 0.5 209492 10548 ? Sl 20:58 0:00 /usr/bin/qemu-system-i386 -xen-domid 0 -xen-attach -name dom0 -nographic -M xenpv -daemonize -monitor /dev/null -serial /dev/null -parallel /dev/null -pidfile /var/run/qemu-dom0.pid
创建客户机操作系统 DOMU
确认主机操作系统正常运行后,就可以部署客户机操作系统 DOMU 了。
我参考《Linux 内核精髓-精通 Linux 内核的必会的 75 个绝技》一书,使用如下配置文件:
kernel = "/boot/vmlinuz-4.19.0-9-amd64"
ramdisk = "/boot/initrd.img-4.19.0-9-amd64"
memory = 1024
name = "debian"
disk = [ 'file:/home/longyu/xen.img,xvda1,w' ]
root = "/dev/xvda1,ro"
extra = " 3"
制作一个简单的根文件系统
上述配置文件中,xen.img 根文件系统是我按照《Linux 内核精髓-精通 Linux 内核的必会的 75 个绝技》一书指导制作的,主要的步骤如下:
- 使用 dd 生成一个固定大小的 xen.img 文件
- 使用 mkfs.ext4 为 xen.img 进行分区
- 挂载 xen.img 到 /mnt 目录下,拷贝一些必要的文件与目录到其中,然后卸载
这个 xen.img 相当于 rootfs,我们需要拷贝 /usr、/etc/、/bin 等信息,并根据情况修改 initab 脚本。
创建 xen 虚拟机
完成了 rootfs 的制作后,就可以执行 xl 命令创建虚拟机了。我执行如下命令创建一个配置文件为 debian 的 xen 虚拟机。
sudo xl create -c /etc/xen/debian
执行后开始打印内核初始化信息,在执行挂载 rootfs 的操作时报 No such file or directory 的错误。这个 No such file or directory 问题可能是 /dev/xvda1 设备文件没有生成,或者 /root 目录没有创建,根据我之前对 debian 启动过程的研究,我判断这个问题大概率应该是 /dev/xvda1 设备文件没有生成。
修改 initrd 确认问题
为了进一步确认问题,我修改 initrd 文件,在其中 init 脚本中直接调用 sh,这样下一次引导的时候就能够在 shell 中手动执行一些操作,确认问题。
我重新执行命令创建 xen 虚拟机,进入 shell 后,我直接 ls /dev/xvda1 ,发现确实没有这个文件。这个问题也不算是啥大问题,应该是某些驱动模块没有加载导致 /dev/xvda1 设备文件没有生成。
/dev/xvda1 设备文件如何生成
在之前研究 debian 的启动过程时,我发现设备文件的创建首先要加载相关的模块,模块内部可能有自动创建的处理,也可能需要 systemd-udevd 守护进程来创建。我搜索虚拟机中的进程,发现没有找到 systemd-udevd 守护进程。
百度了一下,发现可以执行 xl block-list 来显示 xen 虚拟机中的 block 设备列表,命令执行示例如下:
longyu@virt-debian10:~/xen-4.11.3+24-g14b62ab3e5/docs/misc$ sudo xl block-list debian
Vdev BE handle state evt-ch ring-ref BE-path
51713 0 11 1 -1 -1 /local/domain/0/backend/vbd/11/51713
根据这个结果,我确认 xen 虚拟机中已经 attach 了 xen.img block 设备。《Linux 内核精髓-精通 Linux 内核的必会的 75 个绝技》一书中在制作 initrd 的时候提到要指定 xennet、xenblk 驱动加入到 initrd,我没有在我的系统中搜索到这两个内核模块,同时也没有注意到其中提到的前端驱动程序的说法。
我判断这个问题应该是没有加载一些与 xen 相关的驱动。
于是我搜索 /lib 目录,搜索以 xen 开始的 .ko 文件,果然搜索到了一些。我同时查看宿主机中加载的 xen 模块,得到了如下信息:
longyu@virt-debian10:~$ sudo lsmod | grep xen
xen_netback 65536 2
xen_blkback 49152 1
xen_gntdev 24576 1
xen_evtchn 16384 2
xenfs 16384 1
xen_privcmd 24576 35 xenfs
我尝试在 xen 虚拟机中执行 modprobe xen-blkback,加载了后发现没有任何效果,同时继续加载 scsi_mod 与 sd_mod 模块,发现 /dev/xvda1 设备文件仍旧没有生成,这让我非常困惑。网上搜索了一下也没有找到类似的问题,感觉应该是要加载 xen 相关的一些内核模块就能生成,但是结果却失败了。
xen-blkfront 内核模块
一段时间的尝试后,我注意到了启动 xen 虚拟机时终端输出的如下行:
xenbus_probe_frontend: Device with no driver: device/vbd/51713
看到这一行,我确定这个问题就是没有加载需要的驱动。我继续在 /lib 中搜索 xen 相关的模块,这时我终于注意到了 xen-blkfront.ko 这个内核模块。我之前试过加载 xen-blkend.ko 没成功,但是可不可能是加载的模块不对呢?
我执行 modprobe 加载 xen-blkfront.ko 文件,终端中有如下输出信息:
Invalid max_queues (4), will use default max: 1.
blkfront: xvda1: flush diskcache: enabled; persistent grants: enabled; indirect descriptors: enabled;
从输出信息来看 /dev/xvda1 设备文件应该正常生成了,我执行 ls 命令查看,果然有了。这时我才意识到其实 Dom0 所在的宿主机中属于 backend,而其它的 xen guest 虚拟机都属于 frontend,故而需要加载 xen-xxfront 这种模块。
挂载根分区失败的问题
成功创建了 /dev/xvda1 设备文件后,我尝试执行 mount /dev/xvda1 /root 挂载根分区,结果却发现失败了。研究了下发现是没有指定分区的文件系统,添加了 -t ext4 后发现仍旧失败,这时我意识到没有加载 ext4 模块,成功加载 ext4 模块后,正常挂载,chroot 能成功执行。
使用 busybox 重新制作 initramfs 文件系统
我修改了 initrd.img-4.19.0-9-amd64 这个 initrd 文件内容,结果却发现进入系统后登录不成功,考虑到宿主机这个 initrd 里面包含了太多东西,我就尝试使用 busybox 来重新制作 initramfs 文件系统。
主要步骤如下:
- 获取 busybox 源码
- make menuconfig 选择静态编译后执行 make 命令进行编译
- make install 安装 busybox 工具链到 _install 目录中
- 修改 _instatll 目录中的文件,拷贝宿主机的 /lib 目录,添加 init 脚本重新制作 initrd
initrd 文件制作的问题
在 initrd 文件制作时,我遇到了一个小问题,这个小问题耽搁了我几个小时的时间。我最开始生成 initrd 文件的过程记录如下:
- cd _install
- find ./ -type f > list
- cpio -o -H newc -O initrd < list
- gzip initrd
表明上看来好像没有啥问题,但是当我尝试用制作出来的 initrd 文件来引导时,却发现内核直接 oops 了。输出信息中关联比较大的信息摘取如下:
Failed to execute /init (error -2)
上述信息表明 /init 脚本执行失败。我在标准错误值中搜索 -2,发现这个错误值表示的是 No such file or directory。我一度以为是 initrd 文件不对,对了下 md5 发现是一样的,重新生成了下发现还是有相同的问题。
在网上搜索了下,发现大部分问题都是说 init 文件没有加可执行权限,而且返回值是 -9。我检查了 initrd 制作目录下的 init 文件,确认权限正常。
我也检查了 list 文件中的项目,看到了一堆 ./libxxx 文件的内容就直接翻到了最后,看到了 init 文件在列表中。在 initrd 制作目录中执行 ls -l /bin/sh 查看 shell 命令也确实是有的。
我静态编译了一个不断循环打印 hello world 的 c 程序,将它命名为 init,重新生成 initrd 文件,重新启动虚拟机,发现可以正常打印。
软链接问题
搞了几个小时,内心是奔溃的,没有搞定就将这个问题跳过了。晚上吃完放之后我又重新来看这个问题,我觉得应该是哪里没有整对,可能真的是没有文件。
有这个预期,我着重观察了下 initrd 制作目录下的文件,发现 /bin 下面除了 busybox 命令之外其它命令全部是指向 busybox 的软链接,我灵机一动,是不是软链接的问题呢?
我重新检查了下 list 中的文件列表,果然没有找到 /bin/sh 与其它的命令,只有 /bin/busybox 文件。
我意识到应该是 find -type f 的问题,不指定 -type f 直接搜索然后重新生成 list,生成后重新检查 list 发现这次有了 /bin/sh 等文件。
其实这里的问题在于 busybox 安装目录下的 /bin/ 目录中的 /bin/sh 命令是指向 busybox 命令的软链接,对于 find 来说它属于链接文件类型,所以 -type f 是不会检索出 /bin/sh 文件的,这意味着 list 中其实根本就没有这个文件,只不过我一直以为 initrd 制作目录下的 bin/sh 文件存在就以为 initrd 中也应该存在,其实两个完全没有关联。
重新生成 initrd 文件后这次终于成功了。
xen 虚拟机的启动脚本
最终我创建的 initrd 文件中的 init 脚本内容如下:
#!/bin/sh
mkdir -p /dev
mount -t devtmpfs -o nosuid,mode=0755 udev /dev
modprobe xen-blkfront
modprobe xen-netfront
modprobe xen-pcifront
modprobe ext4
mkdir -p /root
mount -t ext4 /dev/xvda1 /root
mount -t sysfs -o nodev,noexec,nosuid sysfs /root/sys
mount -t proc -o nodev,noexec,nosuid proc /root/proc
exec chroot /root
上述脚本首先创建 /dev 目录,然后挂载 devtmpfs 虚拟文件系统,这之后再加载 xen-xxfront 驱动来生成设备文件,由于我制作的根文件系统分区格式是 ext4,这里需要加载 ext4 模块,最后挂载 sysfs 与 proc 虚拟文件系统,然后执行 chroot 切根就完成了所有的过程。
我使用的虚拟机配置文件内容如下:
kernel = "/boot/vmlinuz-4.19.0-8-amd64"
ramdisk = "/home/longyu/initrd.gz"
memory = 1024
name = "debian"
disk = [ 'file:/media/xen.img,xvda1,w' ]
root = "/dev/xvda1 rw"
extra = "3"
vif = [ 'mac=00:16:3E:74:34:32', 'mac=00:16:3e:5f:48:e4,bridge=xenbr0' ]
我配置了 bridge 网络,并设置了两个 xen 虚拟机的网络接口。xenbr0 是在宿主机上创建的网桥,网桥的创建与配置的具体过程详见:我的这篇博客
创建完成并配置 ip 地址后,创建虚拟机,执行 ifconfig -a 命令可以看到有 eth0、eth1、lo 这几个网络接口设备。相关信息如下:
root@(none):/# ifconfig -a
eth0: flags=4098<BROADCAST,MULTICAST> mtu 1500
ether 00:16:3e:74:34:32 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth1: flags=4098<BROADCAST,MULTICAST> mtu 1500
ether 00:16:3e:5f:48:e4 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=8<LOOPBACK> mtu 65536
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
我执行 ifconfig eth0 192.168.122.49 来给 eth0 配置 ip 并将接口 up 起来,然后启动 sshd 服务。启动的时候报错说 /run/sshd 目录没有创建,创建后 sshd 命令成功执行。
我在宿主机上执行如下命令连接到 xen 虚拟机内:
[longyu@debian-10:18:44:47] ~ $ ssh longyu@192.168.122.49
ssh: connect to host 192.168.122.49 port 22: No route to host
[longyu@debian-10:19:07:44] ~ $ ssh longyu@192.168.122.49
PTY allocation request failed on channel 0
Linux (none) 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
发现一直卡住不进入终端,使用 netstat -an 确认连接成功建立,使用 strace -p pid 跟踪,发现程序在 select 系统调用上阻塞。注意上面的信息中有如下行:
PTY allocation request failed on channel 0
这个问题以前遇到过,是没有虚拟终端能够分配,应该是我没有挂载 devpts 。我手动执行挂载 devpts :
mkdir /dev/pts
mount -t devpts devpts /dev/pts
断开重新连接,这之后就能够正常登录了。操作记录如下:
^C[longyu@debian-10:19:12:48] ~ $ ssh longyu@192.168.122.49
Linux (none) 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sun Aug 2 01:38:11 2020 from 192.168.122.1
longyu@(none):~$
使用 virt-manager 来管理 xen
在网上搜索资料时有看到 virt-manager 能够用来管理 xen 虚拟机,就尝试了下。首先点击 virt-manager 的 File 菜单,选择 Add Connection,选择后会弹出如下对话框:
在对话框中填入用户名与主机名,并勾选自动连接。结果却发现连接失败了,报错信息如下:
报错信息表明远程主机上 netcat/nc 的版本不支持 -U option,尝试不通过 ssh 连接仍旧失败,报错信息表明不通过 ssh 连接时,xen 虚拟机中要安装某种服务,但是显然我的虚拟机中并没有安装它,这个问题就到此为止。