雖然微軟在 BUILD 2020 上已經宣佈,未來會讓 WSL2 可以執行 GUI 應用程式,但不知道什麼時候才會正式支援這個功能,對於想體驗看看效果到底如何的我呢,就打算先在 Windows 端啓動一個 X Window Server 來嚐鮮看看。
事前準備
首先,對於一個習慣 Ubuntu 的我來說,如果可以體驗到完整的 Ubuntu 桌面是再好不過了,換句話說,我們需要在上面可以跑一個完整的 Gnome Shell 環境,並加上 Ubuntu 的 Extension 們。
取得完整 systemd 環境
Gnome Shell 從 3.34 版開始,就已經跟 systemd 整合了,也就是說,我們必須要先有一個完整的 systemd 環境才能順利的執行 Gnome Shell。由於 WSL2 的實作機制其實是啓動一個 Tiny Linux VM,然後利用 Linux Namespace 機制來執行並隔離各個發行版,並且由微軟實作了一個 init (PID 1) 來辦到快速啓動以及作為與 windows 溝通的橋樑。
也因此,在 WSL2 的系統裡面,其實是沒有啓用 systemd 的,我們可以簡單的透過下面的方式來檢查看看:
$ systemctl
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
$ ps u -q 1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 908 592 ? Sl 10:31 0:00 /init
可以很明顯的看到,當我們執行 systemctl
的時候,會顯示出我們的 init system (PID 1) 並非 systemd,而是微軟提供的 /init
。
那我們如果想要擁有一個 systemd 環境的話,該怎麼辦呢?
由於 systemd 必須以 PID 1 的方式執行,所以直接執行 systemd 是沒有用的,但多虧了 Linux Namespace 我們可以在 WSL2 中建立新的 Namespace 並把 systemd 作為 PID 1 來執行,也就是在 WSL2 中再多加一層 PID Namespace,使得我們可以建築一個 systemd 的環境並跳進這個新的 Namespace 中。
所幸,我們不需要自己來研究這部分該如何操作,GitHub 上已經有幾個專案可以直接拿來參考並使用:
我們下面會使用 DamionGans/ubuntu-wsl2-systemd-script 來當作範例,只要按照說明操作就可以了:
$ git clone https://github.com/DamionGans/ubuntu-wsl2-systemd-script.git
Cloning into 'ubuntu-wsl2-systemd-script'...
remote: Enumerating objects: 76, done.
remote: Counting objects: 100% (76/76), done.
remote: Compressing objects: 100% (55/55), done.
remote: Total 76 (delta 40), reused 41 (delta 21), pack-reused 0
Unpacking objects: 100% (76/76), 19.46 KiB | 996.00 KiB/s, done.
$ cd ubuntu-wsl2-systemd-script/
ubuntu-wsl2-systemd-script $ bash ubuntu-wsl2-systemd-script.sh
[sudo] password for davy:
Hit:1 http://security.ubuntu.com/ubuntu focal-security InRelease
Hit:2 http://archive.ubuntu.com/ubuntu focal InRelease
Hit:3 http://archive.ubuntu.com/ubuntu focal-updates InRelease
Hit:4 http://archive.ubuntu.com/ubuntu focal-backports InRelease
Reading package lists... Done
(Reading database ... 31836 files and directories currently installed.)
Preparing to unpack .../0-dbus-user-session_1.12.16-2ubuntu2.1_amd64.deb ...
Unpacking dbus-user-session (1.12.16-2ubuntu2.1) over (1.12.16-2ubuntu2) ...
Preparing to unpack .../1-dbus-x11_1.12.16-2ubuntu2.1_amd64.deb ...
Unpacking dbus-x11 (1.12.16-2ubuntu2.1) over (1.12.16-2ubuntu2) ...
Preparing to unpack .../2-dbus_1.12.16-2ubuntu2.1_amd64.deb ...
Unpacking dbus (1.12.16-2ubuntu2.1) over (1.12.16-2ubuntu2) ...
Preparing to unpack .../3-libdbus-1-3_1.12.16-2ubuntu2.1_amd64.deb ...
Unpacking libdbus-1-3:amd64 (1.12.16-2ubuntu2.1) over (1.12.16-2ubuntu2) ...
Selecting previously unselected package daemonize.
Preparing to unpack .../4-daemonize_1.7.8-1_amd64.deb ...
Unpacking daemonize (1.7.8-1) ...
Selecting previously unselected package fontconfig.
Preparing to unpack .../5-fontconfig_2.13.1-2ubuntu3_amd64.deb ...
Unpacking fontconfig (2.13.1-2ubuntu3) ...
Setting up fontconfig (2.13.1-2ubuntu3) ...
Regenerating fonts cache... done.
Setting up libdbus-1-3:amd64 (1.12.16-2ubuntu2.1) ...
Setting up dbus (1.12.16-2ubuntu2.1) ...
Setting up daemonize (1.7.8-1) ...
Setting up dbus-x11 (1.12.16-2ubuntu2.1) ...
Setting up dbus-user-session (1.12.16-2ubuntu2.1) ...
Processing triggers for systemd (245.4-4ubuntu3) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for libc-bin (2.31-0ubuntu9) ...
rm: cannot remove '/lib/systemd/system/sysinit.target.wants/proc-sys-fs-binfmt_misc.mount': No such file or directory
'\\wsl$\Ubuntu-20.04\home\davy\ubuntu-wsl2-systemd-script'
是目前用來啟動 CMD.EXE 的目錄路徑。不支援 UNC 路徑。
預設目錄是 Windows 目錄。
成功: 已經儲存指定的值。
'\\wsl$\Ubuntu-20.04\home\davy\ubuntu-wsl2-systemd-script'
是目前用來啟動 CMD.EXE 的目錄路徑。不支援 UNC 路徑。
預設目錄是 Windows 目錄。
成功: 已經儲存指定的值。
ubuntu-wsl2-systemd-script $
安裝完畢後我們需要重啓整個 WSL2,假定我們的 WSL 名稱是 ubuntu-20.04
,在命令提示字元(CMD)中執行下列指令以關閉 WSL2:
> wsl.exe -t ubuntu-20.04
接者使用一般使用者啓動 WSL2,就可以發現有不一樣的地方了(多了一個啓動 systemd 的提示),透過檢查 PID 1 也可以發現整個環境已經是由 systemd 掌握了:
Sleeping for 1 second to let systemd settle
Welcome to Ubuntu 20.04 LTS (GNU/Linux 4.19.104-microsoft-standard x86_64)
$ ps u -q 1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 2.8 0.0 175224 12920 ? Ss 21:48 0:06 /lib/systemd/systemd --system-unit=basic.target
$
移除用不到的 snap(可選)
由於 Ubuntu 安裝 systemd 時,會連 snap 也一併啓用,如果大家用不到的話可以把 snap 從系統中移除,這麼一來也可以提升啓動速度,我們可以看到系統啓動時也連著一些 snap 的元件一起啓動了(而且還不少 Processes):
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 1.3 0.0 108788 11360 ? Ss 22:03 0:00 /lib/systemd/systemd --system-unit=basic.targ
root 43 2.1 0.1 45248 14600 ? S<s 22:03 0:00 /lib/systemd/systemd-journald
root 61 0.4 0.0 21592 8496 ? Ss 22:03 0:00 /lib/systemd/systemd-udevd
systemd+ 63 1.2 0.0 18548 7896 ? Ss 22:03 0:00 /lib/systemd/systemd-networkd
root 152 0.0 0.0 10572 4588 pts/0 S 22:03 0:00 /bin/login -p -f 'HOSTTYPE=x86_64' 'PWD=
root 219 1.2 0.0 3608 1792 ? Ss 22:03 0:00 snapfuse /var/lib/snapd/snaps/core18_1705.sna
root 220 0.3 0.0 3660 1476 ? Ss 22:03 0:00 snapfuse /var/lib/snapd/snaps/lxd_14804.snap
root 221 0.1 0.0 3488 1536 ? Ss 22:03 0:00 snapfuse /var/lib/snapd/snaps/snapd_7264.snap
systemd+ 228 1.1 0.0 24116 12592 ? Ss 22:03 0:00 /lib/systemd/systemd-resolved
root 231 0.1 0.0 241020 9280 ? Ssl 22:03 0:00 /usr/lib/accountsservice/accounts-daemon
message+ 232 0.2 0.0 7428 4640 ? Ss 22:03 0:00 /usr/bin/dbus-daemon --system --address=syste
root 235 0.3 0.1 29216 17788 ? Ss 22:03 0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher
syslog 236 0.1 0.0 224328 4296 ? Ssl 22:03 0:00 /usr/sbin/rsyslogd -n -iNONE
root 238 2.1 0.2 1457156 35432 ? Ssl 22:03 0:00 /usr/lib/snapd/snapd
root 240 1.1 0.0 16852 7728 ? Ss 22:03 0:00 /lib/systemd/systemd-logind
root 261 0.0 0.0 236408 9084 ? Ssl 22:03 0:00 /usr/lib/policykit-1/polkitd --no-debug
root 302 0.0 0.0 8540 2764 ? Ss 22:03 0:00 /usr/sbin/cron -f
root 307 0.4 0.1 108036 20512 ? Ssl 22:03 0:00 /usr/bin/python3 /usr/share/unattended-upgrad
daemon 308 0.0 0.0 3796 2200 ? Ss 22:03 0:00 /usr/sbin/atd -f
root 318 0.0 0.0 7356 2172 tty1 Ss+ 22:03 0:00 /sbin/agetty -o -p -- \u --noclear --keep-bau
root 326 0.0 0.0 5832 1744 ? Ss 22:03 0:00 /sbin/agetty -o -p -- \u --noclear tty1 linux
davy 376 0.2 0.0 18444 9612 ? Ss 22:03 0:00 /lib/systemd/systemd --user
davy 377 0.0 0.0 110132 3188 ? S 22:03 0:00 (sd-pam)
davy 387 0.2 0.0 10048 4992 pts/0 S 22:03 0:00 -bash
davy 431 0.0 0.0 10604 3224 pts/0 R+ 22:04 0:00 ps aux
$
首先我們先將所有 snap 都列出後,一個一個移除:
$ snap list
Name Version Rev Tracking Publisher Notes
core18 20200311 1705 latest/stable canonical✓ base
lxd 4.0.1 14804 latest/stable/… canonical✓ -
snapd 2.44.3 7264 latest/stable canonical✓ snapd
# snap remove lxd
# snap remove core18
# snap remove snapd
$ snap list
No snaps are installed yet. Try 'snap install hello-world'.
# apt purge snapd
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.6 0.0 109692 12692 ? Ss 22:03 0:03 /lib/systemd/systemd --system-unit=basic.targ
root 43 0.1 0.1 53444 15456 ? S<s 22:03 0:00 /lib/systemd/systemd-journald
root 61 0.0 0.0 21592 8496 ? Ss 22:03 0:00 /lib/systemd/systemd-udevd
systemd+ 63 0.1 0.0 18548 7896 ? Ss 22:03 0:00 /lib/systemd/systemd-networkd
root 152 0.0 0.0 10572 4588 pts/0 S 22:03 0:00 /bin/login -p -f 'HOSTTYPE=x86_64' 'PWD=
systemd+ 228 0.0 0.0 24116 12592 ? Ss 22:03 0:00 /lib/systemd/systemd-resolved
root 231 0.0 0.0 241020 9280 ? Ssl 22:03 0:00 /usr/lib/accountsservice/accounts-daemon
message+ 232 0.0 0.0 7428 4640 ? Ss 22:03 0:00 /usr/bin/dbus-daemon --system --address=syste
root 235 0.0 0.1 29216 17788 ? Ss 22:03 0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher
syslog 236 0.0 0.0 224328 4296 ? Ssl 22:03 0:00 /usr/sbin/rsyslogd -n -iNONE
root 240 0.1 0.0 16852 7728 ? Ss 22:03 0:00 /lib/systemd/systemd-logind
root 261 0.0 0.0 236408 9084 ? Ssl 22:03 0:00 /usr/lib/policykit-1/polkitd --no-debug
root 302 0.0 0.0 8540 2764 ? Ss 22:03 0:00 /usr/sbin/cron -f
root 307 0.0 0.1 108036 20512 ? Ssl 22:03 0:00 /usr/bin/python3 /usr/share/unattended-upgrad
daemon 308 0.0 0.0 3796 2200 ? Ss 22:03 0:00 /usr/sbin/atd -f
root 318 0.0 0.0 7356 2172 tty1 Ss+ 22:03 0:00 /sbin/agetty -o -p -- \u --noclear --keep-bau
root 326 0.0 0.0 5832 1744 ? Ss 22:03 0:00 /sbin/agetty -o -p -- \u --noclear tty1 linux
davy 376 0.0 0.0 18444 9708 ? Ss 22:03 0:00 /lib/systemd/systemd --user
davy 377 0.0 0.0 110132 3188 ? S 22:03 0:00 (sd-pam)
davy 387 0.0 0.0 10180 5252 pts/0 S 22:03 0:00 -bash
root 1168 0.0 0.1 283780 15888 ? Ssl 22:07 0:00 /usr/lib/packagekit/packagekitd
davy 1273 0.0 0.0 10604 3316 pts/0 R+ 22:11 0:00 ps aux
結束後我們就可以發現 Processes 數量減少了許多,讓我們的環境又稍微輕量了些。
在 Windows 準備 X Window Server
這邊有很多選擇,我選擇了 X410(付費),大家也可以選擇 VcXsrv 之類的解決方案,這邊就不多贅述了,在這裡的範例中會需要將 X Window Server 開在 Windows 的 6000 port 上。
安裝 Ubuntu 桌面
接著我們就可以來安裝 Ubuntu 的預設桌面了,這裡會需要比較多的硬碟空間:
# apt install ubuntu-desktop
...
10 upgraded, 1077 newly installed, 0 to remove and 64 not upgraded.
Need to get 605 MB of archives.
After this operation, 2260 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
...
安裝結束後,我們需要先取得 Windows 的 IP 位置,並嘗試對 X Window Server 連線看看:
$ cat /etc/resolv.conf
# 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.17.160.1
$ nc -v 172.17.160.1 6000
Connection to 172.17.160.1 6000 port [tcp/x11] succeeded!
^C
$
成功連線後,我們可以撰寫一個啓動腳本,將 DISPLAY
指向 Windows 上的 X Window Server 中並且加上一些 Ubuntu 桌面的設定後執行 Gnome Shell:
$ cat - > gnome.sh <<'EOF'
#!/bin/bash
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0;
export XDG_SESSION_TYPE="x11"
export XDG_RUNTIME_DIR=~/.cache/xdg
export XDG_SESSION_CLASS="user"
export XDG_SESSION_DESKTOP=ubuntu
export XDG_CURRENT_DESKTOP=ubuntu:GNOME
export DESKTOP_SESSION=ubuntu
export GDMSESSION=ubuntu
export GNOME_SHELL_SESSION_MODE=ubuntu
gnome-session "$@"
EOF
$ chmod +x gnome.sh
$ ./gnome.sh
當然,如果想要在這個桌面中執行 Windows CMD 也是沒有問題的,而系統也可以正確的偵測到 Virtualization 是 WSL 呢。
關閉 systemd-resolved
這裡是一個可選的項目,由於筆者在透過 /etc/resolv.conf
取得 Windows IP 時有遇到 resolv.conf
被 systemd-resolved 替換成 127.0.0.53
的問題,這邊提供一個方式將這個東西停用,操作完重啓 WSL2 即可:
# systemctl stop systemd-resolved
# systemctl disable systemd-resolved
Removed /etc/systemd/system/multi-user.target.wants/systemd-resolved.service.
Removed /etc/systemd/system/dbus-org.freedesktop.resolve1.service.
後記
由於目前的 WSL2 還沒有 GPGPU 加速,所以拿來執行 GUI 程式的話可能還是會有點 lag,微軟提出的最終方案是透過 RDP 來執行 GUI 應用程式也許會透過 RemoteApp 來將顯卡加速做在 Windows 端,但作為嚐鮮一下,目前的結果已經算是還可以的了XD