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 .