在 Windows Subsystem For Linux 2 内使用 systemd 的三种方法
一切后果自行承担。
WSL1 时代就有人提出了这个问题,到 WSL2 依然没有解决。
方法一
这个方法只支持 Windows 10 版本号高于 20201 的系统。意味着在本文撰稿时的 20H2 (19042) 无法使用。
在 /etc/wsl.conf
内添加如下内容:
[boot]
command = "sudo daemonize /usr/bin/unshare -fp --mount-proc /lib/systemd/systemd --system-unit=basic.target"
这个方法是由 @Biswa96 发现的。因为在初始化 WSL 之前,/init
进程会加载 wsl.conf
,所以一个子进程会被分出来。这个子进程然后在系统调用层执行这行代码,最后通过 posix_spawn(3) 生成一个进程。
方法二
这个方法来自 wsl2-hacks 这个项目。大致是创建一个 fake shell,然后这个 shell 会截断所有去 wsl.exe bash ...
的调用,并且会将这个请求发送给在 real bash 内运行的 systemd。我在 Debian 10、Ubuntu 20.04、Arch Linux 以及 RHEL 8 里测试通过了。
首先,更新系统并安装必要的包(Debian 系):
$ sudo apt update
$ sudo apt install dbus policykit-1 daemonize
ArchLinux 需要安装 dbus polkit daemonize
(daemonize 需要 archlinuxcn 源,或者手动从 AUR 安装) 这三个包。
RHEL 需要安装 dbus polkit
,而 daemonize
这个包需要从 EPEL 下载并手动安装,可以在这里获取到。
第二步,就是创建一个假的 bash。
$ sudo touch /usr/bin/bash-bootstrap-services
$ sudo chmod +x /usr/bin/bash-bootstrap-services
$ sudo editor /usr/bin/bash-bootstrap-services
接下来,将 <YOURUSER>
替换成你的用户名。
#!/bin/bash
# your WSL2 username
UNAME="<YOURUSER>"
UUID=$(id -u "${UNAME}")
UGID=$(id -g "${UNAME}")
UHOME=$(getent passwd "${UNAME}" | cut -d: -f6)
USHELL=$(getent passwd "${UNAME}" | cut -d: -f7)
if [[ -p /dev/stdin || "${BASH_ARGC}" > 0 && "${BASH_ARGV[1]}" != "-c" ]]; then
USHELL=/bin/bash
fi
if [[ "${PWD}" = "/root" ]]; then
cd "${UHOME}"
fi
# get pid of systemd
SYSTEMD_PID=$(pgrep -xo systemd)
# if we're already in the systemd environment
if [[ "${SYSTEMD_PID}" -eq "1" ]]; then
exec "${USHELL}" "$@"
fi
# start systemd if not started
/usr/bin/daemonize -l "${HOME}/.systemd.lock" /usr/bin/unshare -fp --mount-proc /lib/systemd/systemd --system-unit=basic.target 2>/dev/null
# wait for systemd to start
while [[ "${SYSTEMD_PID}" = "" ]]; do
sleep 0.05
SYSTEMD_PID=$(pgrep -xo systemd)
done
# enter systemd namespace
exec /usr/bin/nsenter -t "${SYSTEMD_PID}" -m -p --wd="${PWD}" /sbin/runuser -s "${USHELL}" "${UNAME}" -- "${@}"
在保存之前,最好看一下 daemonize 的二进制文件在哪里,可以通过 whereis daemonize
来看到。有的发行版在 /usr/bin
下,而有的在 /usr/sbin
下。如果这个写错了会导致重进时 WSL2 宕机。
第三步,将这个 fake bash 设置成用户 root 的 shell。因为我们要 root 权限才能完成这个 hack。
$ sudo editor /etc/passwd
通过编辑 /etc/passwd
来更改 shell,编辑后看起来应该像这样:
root:x:0:0:root:/root:/usr/bin/bash-bootstrap-services
第四步,保存并在 PowerShell 内运行如下命令:
> wsl --shutdown
> ubuntu2004.exe config --default-user root
最后一行根据不同的 Distro,需要运行不同的 exe。例如 Arch 就是 arch.exe
,Debian 就是 debian.exe
。
第五步,就是重新启动你的 WSL2 实例啦!看看是不是和往常一样登入进了自己的用户,并且可以用 systemd 了呢?
$ systemctl is-active dbus
active
$ sudo systemctl status mysql
● mariadb.service - MariaDB 10.3.27 database server
Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
....
如果你还想在实例启动的时候运行一些命令,那么可以写入 /etc/rc.local
这个文件。
$ sudo touch /etc/rc.local
$ sudo chmod +x /etc/rc.local
$ sudo editor /etc/rc.local
创建之后,这么写就行了:
#!/bin/sh -e
# your commands here...
exit 0
注意,/etc/rc.local
只在第一次开启 WSL2 时有效。
方法三
使用 genie。
如果喜欢本文,欢迎点击下方的「鼓掌」按钮!
如果上面没有加载出任何东西,可以点击这里。
如果喜欢本文,欢迎点击下方的「鼓掌」按钮!
如果上面没有加载出任何东西,可以点击这里。