如何从 Windows 安装程序安装 Linux
我不知道你为什么要这么做。大概是和我一样的原因吧?我也不知道是什么原因。
几天前,我在我的 Fediverse 账户上发了一个比较受欢迎的帖子,上面有一张 Windows 安装程序提供安装 Alpine Linux 的图片:
一两天后,我在 YouTube 上发布了一段视频,从头到尾展示了整个安装过程:
https://youtu.be/mJwueWvZRG8
无数人1建议我写一篇博文,介绍我是如何做到这一点的。
第一步:在 NTFS 分区上安装 Linux
如今,这其实很简单!你要选择一个能让你手动挂载分区的 Linux 发行版,然后不加质疑地安装到这些挂载点。据我所知,Arch Linux 和 Alpine Linux 这两个 Linux 发行版可以让你这样做。
分区方案
分区方案大致是这样的:
- 我们使用的是 UEFI/GPT。您可能可以使用传统启动
/MBR
,但这需要您以某种方式从 Windows 安装 grub 启动扇区。我不知道该怎么做。 - 第一个分区是 EFI 分区,大小约为 100MB。你的 Linux 发行版可能希望你创建更大的分区,但 Windows 安装程序只会创建一个 100MB 的分区,所以无论如何你都只能创建这个大小的分区。该分区格式化为 FAT32,并按常规方式挂载到
/boot
。 - 第二个分区是一个 16MB 的分区,我们不需要使用它,但需要正确偏移分区编号,因为 Windows 安装程序会在 EFI 分区和系统分区之间创建一个 16MB 的分区。
- 第三个分区是根文件系统,占满磁盘的其余部分。该分区格式化为 NTFS,使用 ntfs3 分区类型以及 acl 和 windows_names 挂载选项挂载到
/
。acl
标志能让驱动程序使用 POSIX ACL(即文件权限),而 windows_names 标志则能防止创建 Windows 计算机上无法使用的文件(例如,任何包含:
或名为con
的文件)。
制作分区和安装
以 Alpine 半自动安装指南为参考,可以这样创建分区:
// ntfs-3g-progs is required for mkfs.ntfs, which does not ship on
// the alpine live iso for some reason
# apk add ntfs-3g-progs parted
# alias p="parted -sa optimal /dev/sda"
# p mklabel gpt
# p mkpart p 0M 100M
# p mkpart p 100M 116M
# p mkpart p 116M 100%
# p set 1 esp
// required so Windows will detect this as a valid NTFS partition
// later
# p set 3 msftdata
# mkfs.vfat -F32 /dev/sda1
# mkfs.ntfs -Q /dev/sda3
# mount -t ntfs3 -o acl,windows_names /dev/sda3 /mnt
# mkdir /mnt/boot
# mount /dev/sda1 /mnt/boot
之后,你只需按照正常方式继续安装即可。此时,如果你使用的是 Arch Linux,那么你的 pacstrap 命令就会失败,因为 pacman 希望能够创建名称中包含 :s
的文件,而正如我们讨论过的,这是非法的。所以,尽量不要使用依赖于这种能力的 Linux 发行版。本指南的其余部分将假定你安装的是 Alpine。
启动系统
虽然 Alpine 正确地将 rootfstype=ntfs3
添加到了内核 cmdline 中,但由于某些原因,到目前为止,它生成的 initramfs 并不包含 ntfs3 内核模块。此外,Alpine 安装程序会生成一个使用 UUID 标识分区的 fstab。由于我们将依赖 Windows 安装程序创建的分区,而 Windows 安装程序将使用全新的 UUID,因此我们需要更改 fstab,改用不可靠的驱动器名称。
安装完成后,打开 /mnt/etc/fstab
,将 EFI 分区的第一列改为 /dev/sda1,根分区的第一列改为 /dev/sda3
。
接下来,我们需要在 initramfs 中添加 ntfs3。为此,请在实时环境中安装 mkinitfs 软件包,创建一个包含 kernel/fs/ntfs3
文本的文件 /mnt/etc/mkinitfs/features.d/ntfs3.modules
,并确保 /mnt/etc/mkinitfs/mkinitfs.conf
的 features 行包含 ntfs3 功能。
题外话:在撰写本文时,你需要用文本编辑器打开
/sbin/mkinitfs
,并在文件顶部的 sysconfdir 变量前加上/mnt
,因为 mkinitfs 的-b
标志由于某种原因不适用于该变量。我已经就此提交了一份错误报告,希望能得到修复,让我们都能按照GodGates 的旨意,把Alpine Linux 安装到 NTFS 分区上。
之后,运行 mkinitfs -b /mnt -f /mnt/etc/fstab 6.6.28-0-lts
应该可以重新生成 initramfs,并与我们的恶作剧兼容。用 /mnt/lib/modules
中的内容替换内核版本字符串。
最后,我们还需要让 grub 停止使用 UUID。打开 /mnt/boot/grub/grub.cfg
,找到 Alpine Linux 菜单项,将 search ... --set root <UUID of the EFI partition>
替换为 set root='hd0,gpt1'
(该行可能已经存在,如果存在,就这样吧),并将 linux ... root=UUID=<UUID of root partition>
替换为 root=/dev/sda3
。
也许有更好的方法,即修改 /mnt/etc/default/grub
,然后使用 grub-mkconfig 重新生成配置,但我不知道它是如何工作的,所以就这么做了。
看看这个怪物
现在,卸载 /mnt/boot
和 /mnt
,然后就可以重新启动安装新的 Alpine 系统了!卸载非常重要,因为如果在 NTFS 分区上设置了 dirty 位(挂载时是这样),ntfs3
就会拒绝读写挂载,直到你在 Windows 系统上运行 chkdsk
或在 Linux 系统上运行 ntfsfix
。你不会希望你的根分区被挂载为只读分区,这会让你的生活变得艰难。
在确认它确实启动后,我们就可以进入困难的部分了。
第二步:将 Linux 塞到 WIM 文件中
本节(以及本文的其余部分)的灵感来自 Discord 上关于 Windows 安装程序的以下交流:
就上下文而言,WIM(Windows 镜像)文件是微软在 Windows 安装程序中使用的一种压缩存储格式。可以把它想象成安装程序解压缩到硬盘上的精美 ZIP 文件。单个 WIM 文件可以包含多个镜像,Windows 安装程序可以根据产品密钥或您在安装过程中选择的版本来选择要安装的镜像。
因此,我们的目标是在 Windows 安装程序的 WIM 文件(Windows ISO/USB 中的 sources/install.wim
)中添加一个镜像,其内容就是我们的 Linux 安装。
制定计划
创建包含 Linux 安装程序的 WIM 文件非常简单–微软自己就提供了创建 WIM 文件的工具,你可以使用名为 DISM2(部署映像服务和管理工具)的工具来创建自己的 WIM 文件。我一直在使用名为 GImageX 的 DISM 便捷图形用户界面前端,但如果你是微软的纯粹主义者,也可以直接使用 DISM 来达到同样的效果。
bcdboot 不知道 Linux 是什么。它只知道如何让 Windows 启动。我曾想过在 Windows 设置中用安装 GRUB 的东西取代 bcdboot 来解决这个问题,但觉得这有悖于整件事的精神,而且也有点难。相反,我想出了以下策略:
- 在与 Linux 相同的 rootfs 上安装一份 Windows 预安装环境 (WinPE),将它们放在同一个 WIM 映像中
- 让 Windows 安装程序使 WinPE 可启动,并在 Windows 设置的第一阶段结束后将计算机重新启动到 WinPE 中
- 让 WinPE 进行必要调整,使 Linux 可启动,并重新启动进入 Linux
搞定 Windows 预安装环境
WinPE 由微软免费提供,是 Windows 评估和部署工具包的一部分,微软甚至还提供了有关如何下载 WinPE、根据自己的目的定制 WinPE 以及在自定义应用程序中使用 WinPE 的说明。他们唯一不允许你做的就是将其用作通用操作系统,不过这也没什么,因为我们不会这么做。
题外话:你需要使用 Windows 来执行下面的步骤,因为 Windows ADK 不支持其他平台。你也许可以通过其他途径获得 WinPE 的副本,然后使用 wimlib-imagex 从 Linux 中捕获并应用 WIM 文件,但我发现该工具会很乐意从分区中生成文件,而这些分区会导致 Windows 崩溃–因为我们将在 Windows 设置或 WinPE 中使用这些文件,而这两者也会导致文件崩溃,所以这些文件毫无用处。
安装 Windows ADK 和 WinPE 加载项后,以管理员身份从 “开始 “菜单打开 “Deployment and Imaging Tools Environment”,然后运行以下命令来获取 WinPE 的工作副本。
> copype amd64 c:\pe_amd64
这将在 C:\pe_amd64\media\sources\boot.wim
中转存一个包含 Windows PE 的 WIM 文件副本。使用 Dism /Apply-Image
命令或 GImageX
中的 “应用 “选项卡,将此映像应用到包含 Linux rootfs 的驱动器上。本截图中的 Linux rootfs 驱动器为 E:\
:
既然有了 WinPE,我们就可以让它听命于我们。
使 Linux 可启动
UEFI 启动的好处在于,硬盘的前 512 字节不需要任何神奇的启动代码。你可以在固件中注入神奇的 UEFI 启动项(Windows 会注入一个名为 “Windows 启动管理器 “的启动项,而 grub 会注入一个与你在运行 grub-install
时在 --bootloader-id
标志中传递的名称相同的启动项),但你不必这样做。如果直接从硬盘启动,而不借助任何神奇的启动项,UEFI 只需从该硬盘的 EFI 分区读取 efi\boot\bootx64.efi
并运行即可。
除了创建 UEFI 条目外,bcdboot 还会在其中放置一个加载 Windows 的文件。如果在运行 grub-install
时指定--removable
,grub 也会在其中放入一个文件,加载 grub 并从 EFI 分区读取 grub/grub.cfg
。方便的是,Alpine 指定了--removable
,因此无需担心创建神奇的 UEFI 条目。
记得在安装 Linux 时,我们将 /boot
设置为 EFI 分区。因此,要使 Linux 可从 WinPE 启动,我们只需将 Alpine 放入 /boot
的文件放入 EFI 分区即可。它将把efi/boot/bootx64.efi
放到合适的位置,这样从硬盘启动就可以启动grub,grub将加载Alpine。
要做到这一点,首先,我们需要保存一份 Linux EFI 分区的副本。在 Windows 中运行 diskpart
命令,并在 diskpart
提示符下运行以下命令:
> list disk
// note N, the disk number of your Linux drive
> select disk N
> select partition 1
> assign letter=S
这将使 Linux EFI 分区在 Windows 上以 S:\
的形式可用。完成后,你就可以使用 Dism /Capture-Image
命令或 GImageX 中的 “Capture “选项卡将 S:\
驱动器捕获为 Linux rootfs 上的 efi.wim
。
现在我们有了 EFI 分区的内容,只需让 WinPE 将其应用即可。方便的是,WinPE 的默认构建只会在启动时执行 \Windows\System32\startnet.cmd
,因此我们可以让该文件做我们想做的事情。
以管理员身份在记事本中打开 E:\Windows\System32\startnet.cmd
。其中应该已经有一行写着 wpeinit
,顾名思义,就是初始化 WinPE 环境。我不知道它是否必要,但也无妨,所以我就把它留在里面了。添加以下几行:
diskpart /s mount-efi.txt
dism /Apply-Image /ImageFile:"C:\efi.wim" /Index:1 /ApplyDir:S:\
bcdedit /delete {bootmgr} /f
exit
第二行应该不言自明–我们正在将捕获的 EFI 映像应用到 S:\
驱动器,这应该就是我们的 EFI 分区所在的位置。第一行就是我们用来把它放在那里的。默认情况下,EFI 分区不分配盘符,但正如我们上面所做的,你可以使用 diskpart
给它们分配一个盘符。通过 /s
标志,你可以将脚本交给 diskpart 执行,而不是让它从终端读取命令。在 System32
文件夹中创建名为 mount-efi.txt
的文件,并赋予其以下内容以挂载 EFI 分区:
select disk 0
select partition 1
assign letter=S
exit
我们在 startnet.cmd
中添加的第三行删除了 Windows 安装程序为启动 Windows 而创建的神奇 UEFI 启动项。与直接从硬盘启动相比,该条目具有更高的优先级,因此我们希望删除它,以便按照我们的要求启动 grub
。
应该就是这样了!现在,启动到我们的 Linux rootfs 将启动 WinPE 副本,它将使自己无法启动,并使 Linux 代替它启动,然后重新启动。
还有一件事
这是我花了三天时间绞尽脑汁、反复试验、放弃、重试才弄明白的。此时,你可以将 Linux rootfs 截取到 WIM 文件中,然后将其放入 Windows ISO 并从中启动。安装程序似乎会成功运行,直到安装程序问你要产品密钥,而你说没有。然后你就会看到这个界面:
事实证明,Windows 安装程序会从 WIM 文件和你选择的特定镜像文件中读取 EULA。它会在\Windows\System32\[Locale]\Licenses\[Channel]\[Edition]\license.rtf
中查找该EULA。Locale 是语言和国家代码,如 en-US
,channel 是 _Default
、OEM
或 Volume
之一,Edition 是捕获 WIM 文件的软件为映像设置的。我以为这与 GImageX 中的 SKU 下拉菜单相对应,但似乎并非如此,而是由 DISM 自动检测的。WinPE 的通道是 WindowsPE。
因此,请下载一份 RTF 格式的 GNU General Public License v2.0,在 WordPad 中打开它,将字体设置为 Segoe UI
,以便正确显示,然后将其保存为 E:\Windows\System32\en-US\Licenses\WindowsPE\_Default\license.rtf
,其中 E:
是你的 Linux rootfs。你可能还需要填充 OEM 和 Volume 目录 – 我不确定安装程序读取的是哪一个,因为我在测试中填充了所有三个目录。
捕获
现在,我们可以使用 Dism /Capture-Image
或 GImageX 的 “捕获 “选项卡将 Linux rootfs 捕捉到 Windows 设置中。将 Windows ISO 的内容复制到一个方便的文件夹中(我使用的是 C:\win10
,因为我使用的是 Windows 10 ISO),然后将 Linux rootfs 驱动器捕获到该文件夹中的 sources/install.wim
。如果你想在 WIM 文件中保留现有的 Windows 映像,请使用 “附加 “模式;如果你想完全覆盖文件并只能安装 Linux,请使用 “创建 “模式。
就是这样!现在,该目录下的 Windows 安装程序就能安装高山 Linux 了。或者说,它将能够安装 Windows PE 的一个副本,而 Windows PE 附带了整个 Alpine Linux 安装程序,它的唯一任务就是让 Alpine Linux 的副本可以启动。一样的东西。
你可以使用 Windows ADK 中提供的 oscdimg.exe
命令将该文件夹转化为可启动 ISO。在 ElevenForum 的这篇精彩文章中就有相关说明。我很高兴我找到了这篇帖子,因为我不知道该如何指望任何人找出该命令中的那些神奇值。为了防止论坛帖子失效,Wayback Machine 上有该 URL 的存档。
第三步:好处?
此时,你应该可以启动 ISO 并使用 Windows 安装程序将 Alpine Linux 安装到空白硬盘上。请注意,出于各种原因,我不建议在重要的事情上使用这个安装程序:
- 安装程序非常脆弱。我基本上是将磁盘和分区布局硬编码到 Linux 安装程序以及 WinPE 运行的 diskpart 命令中。这只能在 Windows 安装程序接触计算机第一块硬盘之前,将其安装到完全空白的硬盘上。如果安装程序更改了在空白硬盘上创建的分区布局,那就完了。在此说明一下,我使用的安装程序是微软网站上的 Windows 10 22H2 ISO。
- WIM 捕捉程序不会保留 UNIX 文件权限,因此当安装的 Linux 副本启动时,你需要以某种方式修复整个安装过程的权限。
- 对 grub 的更改非常麻烦,我不知道它们是否能在更新后继续使用。
- 老实说,在 Linux rootfs 中使用 NTFS 实在不是时候。正如我之前提到的,如果设置了 “脏位”(dirty bit),就会使整个 rootfs 成为只读文件,从而无法正常使用电脑,直到你从 Live CD 启动来清除它。此外,虽然基本的 Alpine 安装可以正常运行,但并不能保证你安装的任何软件都不会创建被 Windows 禁用的文件名。
- 实际上,你已经按正常方法安装了 Alpine,这是其中的一个子程序。请照着做。谢谢。
但你不得不承认,这很有趣。
请把仇恨邮件转到 “spam\x40tends\x2eto”.:)
本文文字及图片出自 How to install Linux from a Windows installer