WSLg 做了什么
Published:
WSL2/WSLg 的系统架构1就不谈了, 大家都知道 WSL2 发行版基本就是一个 Hyper-V 虚拟机里的 Linux , 加上与宿主 Windows 的互联互通, 与 WSLg 系统的互联互通.
今天研究下 WSL2 发行版比 Hyper-V 里安装的 Linux 多了些什么, 才能做到上述的互联互通.
Hyper-V 里面只有一个虚拟机即系统发行版, 而所有的用户发行版都是系统发行版下用 namespace 隔离出的子系统
用户发行版与系统发行版
用户发行版: 用户安装的 Linux
PS> wsl
$ uname -a
Linux 5.15.133.1-microsoft-standard-WSL2 #1 SMP Thu Oct 5 21:02:42 UTC 2023 x86_64 GNU/Linux
$ cat /etc/os-release
NAME="Arch Linux"
系统发行版: WSLg 系统
PS> wsl --system
wslg$ uname -a
Linux 5.15.133.1-microsoft-standard-WSL2 #1 SMP Thu Oct 5 21:02:42 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
wslg$ cat /etc/os-release
NAME="Common Base Linux Mariner"
VERSION="2.0.20230630"
Hyper-V 里面只有一个虚拟机即系统发行版, 而所有的用户发行版都是系统发行版下用 namespace 隔离出的子系统
- 用户发行版类似于运行在 docker 容器中2
- 这可以解释所有的跨系统硬链接
- 在系统发行版运行
top
可以确认
kernel 内核修改
用户发行版既然是运行在 namespace 容器中, 自然没有自己的内核.
进入用户发行版, ls /boot
是看不见任何文件的, 实际的启动内核在 C:\Program Files\WSL\tools
.
WSLg 系统的内核 Windows 内有一份:
$ ls -l "/mnt/c/Program Files/WSL/tools"
总计 18172
-r-xr-xr-x 1 user1 user1 2105816 12月 1日 08:34 init
-r-xr-xr-x 1 user1 user1 2106368 12月 1日 08:34 initrd.img
-r-xr-xr-x 1 user1 user1 14387168 10月 5日 21:23 kernel
这个内核除了精简了不需要的驱动以外, 只加了一个驱动3 drivers/gpu/dxgkrnl
, 这个驱动会生成 /dev/dxg
.
这个内核应该是 WSLg 系统和所有已安装的用户发行版共享的.
启动用户发行版 namepace
- 创建 namespace
- 挂载用户发行版的 vhdx
- 挂载
/dev/*
和必要的文件目录
/init
启动程序
WSLg 启动用户发行版后在其中运行 /init
.
内核启动后, 会复制一份 /init
并启动.
- 没有启用 systemd 的系统
/init #wsl init ├─ plan9 --control-socket 5 --log-level 4 --server-fd └─ /init └─ /init └─ -bash -- user1
启动网络文件服务器
可以看见一个进程plan9 --control-socket 5 --log-level 4 --server-fd
, 这是从 WSL1 时代就有的, 用于宿主 Windows 文件互通的网络文件系统4, 叫做Plan 9 file server
.链接与挂载 WSLg 内的文件- 所有指向系统发行版的挂载都是系统发行版通过 namespace 挂载的
设备文件(/dev/dxg
) 这个是个跨系统硬链接socket 文件(/tmp/.X11-unix/X0
) 注意一下,/tmp/.X11-unix
是目录的跨系统硬链接驱动文件(/usr/lib/wsl/drivers
)其他(/mnt/wslg/*
)
$ ls -lda /tmp/.X11-unix $XDG_RUNTIME_DIR/wayland* $XDG_RUNTIME_DIR/pulse/* srwxrwxrwx 1 user1 user1 0 Mar 6 12:37 /mnt/wslg/runtime-dir/pulse/native -rw------- 1 user1 user1 3 Mar 6 12:37 /mnt/wslg/runtime-dir/pulse/pid srwxrwxrwx 1 user1 user1 0 Mar 6 12:37 /mnt/wslg/runtime-dir/wayland-0 -rw-rw---- 1 user1 user1 0 Mar 6 12:37 /mnt/wslg/runtime-dir/wayland-0.lock drwxrwxrwx 2 root root 60 Mar 6 12:37 /tmp/.X11-unix $ stat /tmp/.X11-unix /mnt/wslg/.X11-unix File: /tmp/.X11-unix Size: 60 Blocks: 0 IO Block: 4096 directory Device: 0,94 Inode: 2 Links: 2 Access: (0777/drwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2024-03-06 12:37:37.810283280 +0800 Modify: 2024-03-06 12:37:37.954905785 +0800 Change: 2024-03-06 12:37:37.954905785 +0800 Birth: - File: /mnt/wslg/.X11-unix Size: 60 Blocks: 0 IO Block: 4096 directory Device: 0,94 Inode: 2 Links: 2 Access: (0777/drwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2024-03-06 12:37:37.810283280 +0800 Modify: 2024-03-06 12:37:37.954905785 +0800 Change: 2024-03-06 12:37:37.954905785 +0800 Birth: -
- 设置环境变量
$ env | grep -E "DISPLAY|PULSE|XDG" DISPLAY=:0 WAYLAND_DISPLAY=wayland-0 PULSE_SERVER=unix:/mnt/wslg/PulseServer XDG_RUNTIME_DIR=/mnt/wslg/runtime-dir
- 启用 systemd 的系统
/sbin/init #systemd init │ ├─ /init #wsl init │ ├─ ├─ plan9 --control-socket 5 --log-level 4 --server-fd │ │ └─ /init │ │ └─ -bash -- user1 │ └─ login -- user1 │ └─ -bash ├─ /usr/lib/systemd/systemd-journald ├─ #systemd mount ├─ #systemd tmpfiles
对于启用 systemd 的系统, 其后系统服务可能会挂载
/tmp
和/run/user/1000
, 并重新设置XDG_RUNTIME_DIR
, 使得/init
创建的部分链接失效, 需要正确设置 systemd 来重新创建.$ env | grep -E "DISPLAY|PULSE|XDG" DISPLAY=:0 WAYLAND_DISPLAY=wayland-0 PULSE_SERVER=unix:/mnt/wslg/PulseServer XDG_RUNTIME_DIR=/run/user/1000/ $ ls -lda /tmp/.X11-unix $XDG_RUNTIME_DIR/wayland* $XDG_RUNTIME_DIR/pulse/* lrwxrwxrwx 1 user1 user1 34 Mar 6 12:49 /run/user/1000//pulse/native -> /mnt/wslg/runtime-dir/pulse/native lrwxrwxrwx 1 user1 user1 31 Mar 6 12:49 /run/user/1000//pulse/pid -> /mnt/wslg/runtime-dir/pulse/pid lrwxrwxrwx 1 user1 user1 31 Mar 6 12:49 /run/user/1000//wayland-0 -> /mnt/wslg/runtime-dir/wayland-0 lrwxrwxrwx 1 user1 user1 36 Mar 6 12:49 /run/user/1000//wayland-0.lock -> /mnt/wslg/runtime-dir/wayland-0.lock lrwxrwxrwx 1 root root 19 Mar 6 12:49 /tmp/.X11-unix -> /mnt/wslg/.X11-unix
设备硬链接设备文件共享可能导致的问题
研究发现, WSL2 的许多设备文件(以 /dev/dri/card0
为例), 是同一个文件在多个系统内的硬链接.
wslg$ stat /dev/dri/card0
File: /dev/dri/card0
Size: 0 Blocks: 0 IO Block: 4096 character special file
Device: 5h/5d Inode: 94 Links: 1 Device type: e2,0
Access: (0660/crw-rw----) Uid: ( 0/ root) Gid: ( 44/ UNKNOWN)
Access: 2024-03-08 18:31:17.232796176 +0800
Modify: 2024-03-08 18:31:17.232796176 +0800
Change: 2024-03-08 18:31:17.232796176 +0800
Birth: -
Arch$ stat /dev/dri/card0
File: /dev/dri/card0
Size: 0 Blocks: 0 IO Block: 4096 character special file
Device: 0,5 Inode: 94 Links: 1 Device type: 226,0
Access: (0660/crw-rw----) Uid: ( 0/ root) Gid: ( 44/ UNKNOWN)
Access: 2024-03-08 18:31:17.232796176 +0800
Modify: 2024-03-08 18:31:17.232796176 +0800
Change: 2024-03-08 18:31:17.232796176 +0800
Birth: -
Ubuntu$ stat /dev/dri/card0
File: /dev/dri/card0
Size: 0 Blocks: 0 IO Block: 4096 character special file
Device: 0,5 Inode: 94 Links: 1 Device type: 226,0
Access: (0660/crw-rw----) Uid: ( 0/ root) Gid: ( 44/ video)
Access: 2024-03-08 18:31:17.232796176 +0800
Modify: 2024-03-08 18:31:17.232796176 +0800
Change: 2024-03-08 18:31:17.232796176 +0800
Birth: -
这很可能会导致设备文件用户组混乱的问题, 其中一个系统会突然无法使用 GPU 设备5.
- 多个系统的同名 group 的 GID 不太可能相同
Arch$ cat /etc/group | grep video video:x:110:user1 Ubuntu$ cat /etc/group | grep video video:x:44:user1
- 后一个启动的用户发行版会更改设备文件的用户组
比如 Ubuntu 就把/dev/dri/card0
改为属于 gid=44, 在先启动的 Arch 看来,/dev/dri/card0
就不属于 Arch 的 video, 那么普通用户就无法使用 GPU.
应该使用 user_namespaces
的 /proc/1/gid_map
来转换 gid .