利用 DistroLauncher 在 WSL 上运行自己喜欢的 GNU/Linux 发行版, ,例如还是咱喜欢的 Arch 🌝

复习(?):Windows Subsystem for Linux 是个啥玩意?

从 Windows 10 Insider Preview 开始,加入了 Windows Subsystem for Linux (适用于 Linux 的 Windows 子系统) 功能.

Windows Subsystem for Linux(简称WSL)是一个为在Windows 10上能够原生运行 Linux 二 进制可执行文件(ELF 格式)的兼容层。 它是由微软与 Canonical 公司合作开发,目标是使纯正的 Ubuntu 映像能下载和解压到用户的 本地计算机,并且映像内的工具和实用工具能在此子系统上原生运行。

时光荏苒, WSL 本身也有了第二次演进。 最初的 WSL 提供了一个微软开发的 Linux 兼容内核接口(不包含Linux代码),来自 Ubuntu 的用户模式二进制文件在其上运行。

WSL 2 中的 Linux 内核是根据最新的稳定版分支(基于 kernel.org 上提供的源代码)在内部 构建的。此内核专门针对 WSL 2 进行了优化。 (汝大概可以当作 M$ 专门整了个 Linux 内核 然后再塞进一个专门的虚拟机里来运行 WSL 和下面的一堆发行版)。

有关 WSL 1 和 2 的区别可以看 M$ 官方的对比图。

WSL 的具体应用就是 Bash on Ubuntu on Windows 啦,在 Windows 上实现了一个 Ubuntu 子 系统。(当然后面也支持了几个别的发行版)

开始之前

这不是一篇教授如何使用 WSL 的教程,所以汝应该先把 WSL 装上,然后有一个可用的发行版。

接下来去把 Visual Studio 装上(担心空间不够就只先安装一个编辑器, 接下来打开 DistroLuncher 的时候应该会提醒汝装剩下的)

接着把 DistroLauncher 下载(或者克隆下来),在 Visual Studio 里打开解决方案。

准备发行版 tarball

考虑到 Microsoft Store 里有好几个打包好的发行版了(像是 Ubuntu/Debian/Fedora/ openSUSE 啥的),咱还是拿咱自己中意的 Arch 举例好了。

以及可以参考 ArchWiki 上从现有 GNU/Linux 发行版安装的那一节。

然后用汝手上现有的 WSL 发行版来完成接下来的操作。(咱个人比较中意 Debian)

首先下载 Bootstrap 用的 tarball ,并把它解压到哪里去。(例如 /tmp 或者 /root )

# tar xzf <path-to-bootstrap-image>/archlinux-bootstrap-*-x86_64.tar.gz

用文字编辑器打开 /root.x86_64/etc/pacman.d/mirrorlist ,选取一个镜像仓库。

如果汝所使用的发行版有 Bash 4,而且 unshared 支持 --fork 和 --pid 选项的话,可以 直接用 tarball 里面的 arch-chroot 命令进入 chroot 环境:

# 为了避免 error: could not determine cachedir mount point /var/cache/
  pacman/pkg 错误,要把 tarball 的目录原地绑定挂载一遍。
  (当然汝也可以通过修改 /etc/pacman.conf 关掉安装前的剩余空间检查来绕开
  这个问题,不过不推荐就是了……)
# mount --bind /tmp/root.x86_64 /tmp/root.x86_64
# /tmp/root.x86_64/bin/arch-chroot /tmp/root.x86_64/

不然传统的方法也是能用的,例如:

# mount --bind /tmp/root.x86_64 /tmp/root.x86_64
# cd /tmp/root.x86_64
# cp /etc/resolv.conf etc
# mount -t proc /proc proc
# mount --make-rslave --rbind /sys sys
# mount --make-rslave --rbind /dev dev
# mount --make-rslave --rbind /run run
# chroot /tmp/root.x86_64 /bin/bash

初始化一下 pacman 的密钥环:

# pacman-key --init

# pacman-key --populate archlinux

更新,和安装一些别的软件包,看汝自己的需要啦(--needed 选项会跳过已是最新的 软件包而不是重新安装它们):

# pacman -Syu base base-devel nano --needed

如果有需要的话,汝也可以在这个时候干些别的,例如修改 /etc/sudoers 或者把 linux 包卸载掉之类的 (反正也用不上嘛……)

接着把修改好的 tarball 打包:

# tar --numeric-owner -cvzf /path/to/your/arch.tar.gz /tmp/root.x86_64/

然后把 tarball 复制到汝的 DistroLauncher 下面备用,名字起作 install.tar.gz 。

  • 比较新的 Windows Insider Preview 的话能通过文件资源管理器直接访问 WSL 的 文件系统,大概像这个样子:
WSL on Windows
  • 不然的话,在 WSL 里复制出来也是 OK 的:

    # cp arch.tar.gz /mnt/c/Users/Horo/source/repos/WSL-DistroLauncher/x64/install.tar.gz

修改 DistroLauncher

在 Visual Studio 里打开 DistroLauncher-Appx/MyDistro.appxmanifest , 、 修改一些基本属性(像是名字啥的)。不过别忘了在 Packaging 那里选择一个 打包用的测试证书(不管是现成的还是临时创建一个都 OK)

Packaging in Manifest

打开 Launcher 下面的 DistributionInfo.h ,修改一些和汝的发行版相关的信息:

namespace DistributionInfo
{
    // The name of the distribution. This will be displayed to the user via
    // wslconfig.exe and in other places. It must conform to the following
    // regular expression: ^[a-zA-Z0-9._-]+$
    //
    // WARNING: This value must not change between versions of your app,
    // otherwise users upgrading from older versions will see launch failures.
    // 在 WSL 中区别汝的发行版的名称,如果汝有计划分发自己制作的发行版,记得之后
    // 不能修改它。
    const std::wstring Name = L"Arch";

    // The title bar for the console window while the distribution is installing.
    // 在运行时命令提示符窗口上方的标题。
    const std::wstring WindowTitle = L"Arch Linux";

    // 下面两个函数在 DistributionInfo.cpp 里,默认是适合类 Ubuntu 系统的,
    // 稍后也会修改。

    // Create and configure a user account.
    // 初始化时创建新用户的函数。
    bool CreateUser(std::wstring_view userName);

    // Query the UID of the user account.
    // 查询 UNIX 用户 ID 的函数
    ULONG QueryUid(std::wstring_view userName);
}

打开 Launcher 下面的 DistributionInfo.cpp ,接着修改和汝的发行版相关的信息: (关键大概就是上面有提到的那个 CreateUser 函数)

bool DistributionInfo::CreateUser(std::wstring_view userName)
{
    // 创建用户账户的函数。
    DWORD exitCode;
    // std::wstring commandLine = L"/usr/sbin/adduser --quiet --gecos '' ";
    // Arch 这边没有 adduser ,所以用 useradd 和 passwd 代替。
    // 记得 commandLine 是要和用户名拼起来的,所以最后面要有个空格。
    std::wstring commandLine = L"/usr/bin/useradd -m -s /bin/bash ";
    commandLine += userName;
    HRESULT hr = g_wslApi.WslLaunchInteractive(commandLine.c_str(), true, &exitCode);

    commandLine = L"/usr/bin/passwd ";
    commandLine += userName;
    hr = g_wslApi.WslLaunchInteractive(commandLine.c_str(), true, &exitCode);
    if ((FAILED(hr)) || (exitCode != 0)) {
        return false;
    }

    // 把用户添加进合适的组中,这个也要看不同的发行版调整。
    // commandLine = L"/usr/sbin/usermod -aG adm,cdrom,sudo,dip,plugdev ";
    commandLine = L"/usr/bin/usermod -aG wheel ";
    commandLine += userName;
    hr = g_wslApi.WslLaunchInteractive(commandLine.c_str(), true, &exitCode);
    if ((FAILED(hr)) || (exitCode != 0)) {

        // 如果前面的两条命令失败的话,如何删除用户?
        // commandLine = L"/usr/sbin/deluser ";
        commandLine = L"/usr/bin/userdel ";
        commandLine += userName;
        g_wslApi.WslLaunchInteractive(commandLine.c_str(), true, &exitCode);
        return false;
    }

    return true;
}

如果汝有意愿换个图标的话,把图标放进 /images 文件夹里。

编辑 DistroLauncher-Appx/DistroLauncher-Appx.vcxproj 文件,修改汝的发行版 的可执行文件的名称(例如 arch.exe ?)

<PropertyGroup Label="Globals">
 ...
    <ProjectName>mydistro</ProjectName>
</PropertyGroup>

检查 DistroLauncher-Appx/MyDistro.appxmanifest 文件,确保……

  • <desktop:ExecutionAlias Alias="mydistro.exe" /> 是汝刚刚决定的名字,例如 <desktop:ExecutionAlias Alias="arch.exe" />
  • 以及每一个 Executable 的值也是那个名字,例如 <Application Id="mydistro" Executable="arch.exe" EntryPoint="Windows.FullTrustApplication">

构建和测试

用 Visual Studio 的“部署解决方案”生成 Appx:

部署解决方案

假如一切顺利的话,新的应用会出现在汝的开始菜单里面:

新的应用会出现在汝的开始菜单里面

打开然后创建 UNIX 用户试一下?

效果?

keyboard_arrow_left 上一篇文章: 切点洋葱 - 搭建一个洋葱服务 keyboard_arrow_right 下一篇文章: Chromium OS 和 Crostini 初体验

需要 JavaScript 支持来使用 Isso 😂