WSL2 图形界面

wsl #x11

VcXsrv、X410 和 Xming 都是用于在 Windows 系统上运行 X11 应用程序的开源 X 服务器,主要作用是在 Windows 系统上为 X11 应用程序提供显示支持。但是考虑到近几年的Xming在维护方面较为落后,而且听说虽然X410需要付费但是可以一直试用,因此我们选择了X410作为我们的X服务器

首先我们需要了解一下,什么是X11,以及这些东西到底是做什么的。

X11 协议

X11 是一种网络透明的窗口系统协议,常用于 Unix/Linux 系统上。它允许图形用户界面的应用程序通过网络将其显示内容发送到 X 服务器。X 服务器负责显示窗口和处理用户输入(如鼠标点击、键盘输入等)。

在这种架构中,X 服务器负责屏幕输出和输入管理,X 客户端(应用程序)与 X 服务器通信来展示图形界面。而X 服务器是一个负责绘制图形和管理窗口的服务。在 Linux 系统上,X 服务器直接运行在本地,提供桌面显示。

当我们在 Windows 上运行 VcXsrv 或 Xming 时,它们就相当于一个虚拟的 X 服务器,模拟 Linux 系统的 X 服务器环境。Linux 应用程序通过 X11 协议将绘图请求发送给 VcXsrv/Xming,后者再把这些请求转化为 Windows 系统可以理解的图形指令,从而在 Windows 上显示。

在 WSL 中,X11 请求是通过 localhost 进行本地转发,因此性能非常高,并且通信延迟较低

WSLg (WSL GUI 支持)

WSL其实是有自己的图形化界面的解决方法的。自 WSL 2 之后,微软推出了原生支持 GUI 应用的 WSLg (Windows Subsystem for Linux GUI) 功能。它无需手动安装 VcXsrv 或 Xming,直接通过 WSL 提供对 Linux GUI 应用程序的原生支持。WSLg 在后台自动管理 X 服务器,使 Linux 应用程序的图形界面可以无缝显示在 Windows 上。

但是这个方法我觉得页面相对比较简陋,示例如下:
image.png

使用X服务器

我使用的是X410,因此下面的内容我将会用X410作为例子,并详细记录我探索的过程。

现在我们看到的页面就是这样了:

image.png

根据官方文档中的说法,我们只需要配置一个环境变量DISPLAY,让wsl中的应用知道将图形请求发送到哪里就行了。

为了简单,我们直接编辑 .bashrc 文件,设置 DISPLAY 环境变量就行了。对于wsl1而言,我们这样做就足够了。

1
2
echo "export DISPLAY=localhost:0.0" >> ~/.bashrc
source ~/.bashrc

这将确保在每次启动 WSL 时,DISPLAY 变量都被正确设置。

但是我使用的是wsl2,这两个最大的区别就是,wsl的localhost和宿主机是同一个,而wsl2是不同的,因此也衍生出后面的mirror-mode网络模式,给我带来了一堆问题

根据官网的说法,我们需要这样做:

1
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0

整个命令的作用就是从/etc/resolv.conf这个文件中提取当前系统的 nameserver IP 地址,并将它与 :0.0 结合,设置为 DISPLAY 变量。这种方法也许是有效的,但是我的情况有点复杂,并没有成功。

但是依旧出现了问题:

1
Error: Can't open display: 10.255.255.254:0.0

我们重新回去看刚才所执行的设置DISPLAY变量的命令,这句命令中获取的主机server的ip是从resolv.conf文件中提取出来的。我们看到,这个文件里面是长这个样子的:

1
2
3
4
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 10.255.255.254

但是我们使用ip route的时候看到的内容是这样的:

1
2
default via 172.18.16.1 dev eth0 proto kernel
172.18.16.0/20 dev eth0 proto kernel scope link src 172.18.31.70

也就是说,此时resolv.conf显示的nameserver并不是指向主机的虚拟网络接口。因此无法显示是正常的。那么这个东西指向的是什么呢?

查了一下资料,resolv.conf这个文件用于指定域名解析(DNS)服务器的地址。目前来看,他指向的是10.255.255.254这个地址。我们返回去使用ip a命令查看的时候,我们看到的是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet 10.255.255.254/32 brd 10.255.255.254 scope global lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:5d:04:f9:94 brd ff:ff:ff:ff:ff:ff
inet 172.18.31.70/20 brd 172.18.31.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::215:5dff:fe04:f994/64 scope link
valid_lft forever preferred_lft forever

我有开始怀疑,这个地址指向的是wls虚拟的 DNS 隧道,然后这个DNS隧道再将信息传递给主机系统的 DNS 服务。我们尝试将wsl2的dns隧道功能关闭之后,这个文件中的内容变成了这样:

1
2
3
4
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.18.16.1

这进一步证实了我刚才的想法。而我们在主机中通过ipconfig命令查看:

1
2
3
4
5
6
7
以太网适配器 vEthernet (WSL (Hyper-V firewall)):

连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::b828:9895:d8cb:57e7%71
IPv4 地址 . . . . . . . . . . . . : 172.18.16.1
子网掩码 . . . . . . . . . . . . : 255.255.240.0
默认网关. . . . . . . . . . . . . :

我们看到,这个确实是WSL所虚拟出来的网络接口的地址。这种变化是因为我关闭了 WSL2 的 DNS 隧道管理。关闭隧道后,WSL2 不再自动生成 10.255.255.254 这样的虚拟 DNS,转而使用与 Windows 主机共享的虚拟网络适配器 (172.18.16.1),通过此适配器进行网络通信和 DNS 查询。

因此为了规避这个问题,我们使用以下命令就能将图形请求发送到正确的接口上:

1
export DISPLAY=$(ip route | grep default | awk '{print $3; exit;}'):0.0

但是当我们同时使用mirrored网络模式和DNS隧道,然后还使用tun模式进行代理的的时候,以上两种方法获取到的主机接口都是错误的(属实是buff叠满了)。此时只能我们手动去配置。因此我的建议是使用nat网络模式,这样会使得一切都变得简单起来。


WSL2 图形界面
http://zerohzzzz.github.io/2024/09/10/WSL2 图形界面/
Author
ZeroHzzzz
Posted on
September 10, 2024
Licensed under