☰
深入剖析 WSL2 启动流程:从 wsl.exe 到 bash 的完整链路
2026/10/12 5:32:28 网站建设 项目流程

深入剖析 WSL2 启动流程:从 wsl.exe 到 bash 的完整链路

【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL

WSL2 的启动是一个横跨 Windows 与 Linux 两个世界、涉及 COM 调用、Hyper-V 虚拟机(HCS)、hvsocket 通信与多级 Linux init 进程的复杂过程。本文以仓库文档 boot-process.md 为骨架,结合 WSL 开源仓库的真实源码,逐层拆解从用户在终端敲下wsl.exe到 bash 就绪的每一步,涵盖虚拟机配置、mini_init / gns / init / session leader / relay 各进程的职责与消息协议,帮助你建立对 WSL2 启动链路的完整认知,并能够循着源码路径继续深入阅读。

启动流程全景:一张序列图看懂整体架构

WSL2 的启动并非"一条命令启动一个进程"那么简单,它本质上包含两条独立但又相互依赖的链路:

  1. Windows 侧:wsl.exe(命令行入口)通过 COM 与运行在会话 0 中的wslservice.exe服务通信,由后者负责创建发行版实例、启动 WSL2 虚拟机、维护与 Linux 侧各进程的 hvsocket 通道;
  2. Linux 侧:虚拟机内的mini_init→gns→init→ session leader → relay → 用户进程(bash)逐级 fork/exec,形成一条完整的进程创建链。

官方文档用下面的 Mermaid 序列图精确描绘了从wsl.exe调用到 bash 启动的完整事件顺序(详见 boot-process.md):

这条链路中各进程的定位与相互连接,还可以参考 index.md 中的组件架构图:wslservice.exe通过 hvsocket 同时连接mini_init与gns,mini_init通过 exec 派生出gns、init与localhost,而init又会继续 exec 出plan9与 session leader,最终由 session leader 派生 relay 去 exec 用户命令。

CreateInstance():发行版实例的创建

当wslservice.exe通过 COM 接收到CreateInstance()调用时,它需要完成三件事(详见 wslservice.exe.md 与 boot-process.md):

1)识别目标发行版

服务会在 Windows 注册表中查询DistributionRegistration(实现见 DistributionRegistration.cpp),匹配方式有两种:

  • 按调用方传入的发行版 ID 精确匹配;
  • 若未提供 ID,则回退使用系统默认发行版。

2)按发行版类型分派

根据发行版是 WSL1 还是 WSL2 走不同路径:

  • WSL1:无需虚拟机,直接在 Windows 侧创建一个 WSL1 实例;
  • WSL2:需要启动(或复用)一个 WSL2 虚拟机,下文单独展开。

3)关联实例与调用进程

将新创建的发行版实例与发起调用的 Windows 进程进行关联(实现见 Lifetime.cpp),以便管理该实例的生命周期。

值得补充的是,wslservice.exe是运行在会话 0、以 SYSTEM 身份运行的 Windows 服务,负责管理 WSL 会话、与 WSL2 虚拟机通信以及配置发行版。客户端通过其 COM 接口ILxssUserSession(定义见 wslservice.idl)与之交互;CoCreateInstance()由LxssUserSessionFactory(见 LxssUserSessionFactory.cpp)处理,同一 Windows 用户多次调用会返回同一个LxssUserSession实例(见 LxssUserSession.cpp)。除CreateInstance()外,该接口还提供CreateLxProcess()(在发行版内启动进程)、RegisterDistribution()(注册新发行版)、Shutdown()(终止全部发行版)等关键方法。

启动 WSL2 虚拟机:HCS 与虚拟机配置 JSON

启动 WSL2 发行版的前提是存在一个 WSL2 虚拟机。如果虚拟机尚未运行,它会作为CreateInstance()调用的一部分被创建出来。

通过 HCS 服务创建虚拟机

虚拟机的创建依托 Windows 的 Host Compute System(HCS)服务,核心逻辑位于 WslCoreVm.cpp。流程如下:

  1. wslservice.exe生成一段描述虚拟机配置的JSON 字符串;
  2. 将该 JSON 传给HcsCreateComputeSystem()创建新虚拟机;
  3. HCS 的 JSON schema 细节可参阅 hcs_schema.h。

JSON 配置中的三个关键部分

虚拟机配置 JSON 至少包含以下内容(见 boot-process.md):

配置项说明
内核(kernel)WSL 默认使用内置内核,通常安装于C:\Program Files\WSL\tools\kernel;若用户在.wslconfig中覆盖了kernel=配置项,则使用自定义内核
initramfsWSL 使用自带的 initramfs(通常位于C:\Program Files\WSL\tools\initrd.img),其中只包含 mini_init 这一个二进制
虚拟化资源虚拟机可访问的 CPU、内存(RAM)、GPU 等资源配额

虚拟机启动后,会先引导进入上述内核,随后执行 initramfs 中的 mini_init——这是 WSL2 虚拟机内的第一个用户态进程,也是整个 Linux 侧启动链的起点。

Linux 启动过程:mini_init 与两级配置消息

mini_init:虚拟机内的用户态初始化

mini_init是 WSL2 虚拟机启动后执行的第一个可执行文件。与标准 Linux init 类似,它启动时首先挂载/proc、/sys、/dev等标准挂载点,随后执行一系列配置,包括:启用崩溃转储收集、通过/dev/console配置日志、进行 tty 配置等(详见 mini_init.md)。

一切就绪后,mini_init会向wslservice.exe建立两条 hvsocket 通道:

  • mini_init 通道:用于接收wslservice.exe发来的消息。常见消息包括LxMiniInitMessageLaunchInit(挂载虚拟磁盘并启动新发行版)、LxMiniInitMessageMount(在/mnt/wsl下挂载磁盘,用于wsl --mount)、EJECT_VHD_MESSAGE(弹出磁盘)、LxMiniInitMessageImport/LxMiniInitMessageExport(导入 / 导出发行版)等;
  • 通知通道:用于向wslservice.exe发送通知,主要用来上报 Linux 进程的退出事件,wslservice据此判断发行版是否已终止。

所有消息与响应的完整枚举定义在 lxinitshared.h,例如:

LxMiniInitMessageLaunchInit, // 挂载发行版 VHD 并启动 init LxMiniInitMessageImport, LxMiniInitMessageImportInplace, LxMiniInitMessageExport, LxMiniInitMessageCreateInstanceResult, LxMiniInitMessageEjectVhd, LxMiniInitMessageEarlyConfig, // 早期配置 LxMiniInitMessageInitialConfig, // 初始配置 LxMiniInitMessageMount, LxMiniInitMessageUnmount, ...

LxMiniInitMessageEarlyConfig:早期配置消息

在执行完自身初始化后,mini_init会收到wslservice.exe发来的LxMiniInitMessageEarlyConfig消息(见 boot-process.md),其中携带:

  • 系统 VHD、交换 VHD 以及(如有)内核模块 VHD 的标识符;
  • 虚拟机主机名(hostname);
  • 配置的内存回收模式(memory reclaim mode)与页上报顺序(page reporting order)。

在源码 main.cpp 的LxMiniInitMessageEarlyConfig处理分支中可以看到该消息的更多实际用途:EnableSafeMode会开启安全模式并禁用大量特性;IsolateDistroCgroup且宿主支持 cgroup v2 时调用SetupWslUserCgroup()设置用户 cgroup;随后建立供 guest 网络服务使用的 hvsocket 连接;若启用了 DNS 隧道(EnableDnsTunneling),还会单独再开一条 hvsocket 连接用于 DNS 隧道。

gns:网络配置进程

收到 EarlyConfig 后,mini_init会fork()并exec("/gns")派生 gns 进程,负责虚拟机内的网络配置。wslservice.exe通过LxGnsMessageInterfaceConfiguration向gns下发配置,gns以LxGnsMessageResult回执结果。

WSL2 的网络设置对所有发行版共享。只要 WSL2 在运行,gns就与wslservice.exe保持一条 hvsocket 通道,用于下发并应用:接口 IP 配置、路由表项、DNS 配置、MTU 大小等。当启用 DNS 隧道时,gns还负责直接应答 DNS 请求。相关实现可参见 GnsEngine.cpp(Linux 侧)与 GnsChannel.cpp(Windows 侧)。

LxMiniInitMessageInitialConfig:初始配置消息

随后mini_init会收到LxMiniInitMessageInitialConfig消息,其中包含(见 boot-process.md):

  • 熵缓冲区(entropy buffer):用于为虚拟机熵池播种;
  • GPU 驱动共享(GPU driver shares)信息:如有,则指定需要挂载的 GPU 驱动共享;
  • 是否启用 wslg(WSL 图形界面支持)。

其他维护任务

此外,mini_init还承担多项维护任务(见 mini_init.md):

  • 回收未使用的内存(对应 EarlyConfig 中的内存回收配置);
  • 启动调试 shell tty;
  • 在虚拟机终止时同步 IO;
  • 调整文件系统大小(供wsl --manage <distro> --resize使用);
  • 格式化磁盘(安装新发行版时使用)。

在应用完wslservice.exe请求的全部配置之后,虚拟机即具备启动 Linux 发行版的条件。

启动 Linux 发行版:从 LaunchInit 到 session leader

LxMiniInitMessageLaunchInit:挂载 VHD 并派生 init

要启动一个新的发行版,wslservice.exe会向mini_init发送LxMiniInitMessageLaunchInit消息。mini_init随即挂载发行版 VHD,并在子命名空间中派生并启动 init(详见 init.md)。

从源码 main.cpp 可以看到这条消息的实际处理逻辑:mini_init通过UtilConnectVsock建立到LX_INIT_UTILITY_VM_INIT_PORT的 hvsocket 通道(若启用 GUI 应用还会额外连接一条),随后调用UtilCreateChildProcess创建名为"LaunchDistro"的子进程,子进程在ProcessLaunchInitMessage中真正完成启动,并且创建时使用了如下命名空间克隆标志:

CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD

这正是文档所强调的隔离机制:每个 WSL2 发行版都运行在独立的 mount、pid 与 UTS 命名空间中,互不可见、可并行运行。具体启动顺序是:挂载发行版 VHD → 克隆进子命名空间 → chroot 到 VHD 挂载点 → 执行 init。同时,所有发行版共享同一个/mnt/wsl挂载点。

init:发行版内的顶级进程

init是发行版的顶级进程:WSL1 下由wslservice直接启动,WSL2 下由mini_init启动。启动后init会执行一系列初始化任务(见 init.md):

  • 挂载/proc、/sys、/dev;
  • 配置 cgroups;
  • 注册 binfmt 解释器(与 interop 相关);
  • 解析/etc/wsl.conf;
  • 启动 systemd(见 systemd.md);
  • 挂载 drvfs 驱动器(见 drvfs.md);
  • 配置 wslg。

就绪后,init建立与wslservice的连接(WSL1 走lxbus,WSL2 走hvsocket),并通过该通道接收命令。从 lxinitshared.h 的枚举可以看到完整命令集,例如:

LxInitMessageCreateProcess, // 创建用户进程(WSL1 路径) LxInitMessageCreateSession, // 创建新的 session leader LxInitMessageCreateSessionResponse, LxInitMessageNetworkInformation, LxInitMessageInitialize, // 配置发行版 LxInitMessageCreateProcessUtilityVm, // 创建用户进程(WSL2 路径) LxInitMessageExitStatus, // 进程退出状态上报 LxInitMessageWindowSizeChanged, // 终端窗口尺寸变化 LxInitMessageTerminateInstance, // 终止发行版 ...

LxInitMessageCreateSession在 init.cpp 中处理:initfork()出一个 session leader,并向wslservice.exe回送LxInitMessageCreateSessionResponse。

session leader:面向用户进程的创建者

session leader 是收到LxInitMessageCreateSession消息后从initfork 出来的 Linux 进程(见 session-leader.md 与 init.cpp),每个 session leader 与一个 Windows 控制台(console)关联,代表用户创建 Linux 进程。

创建用户进程时,wslservice.exe发送LxInitMessageCreateProcess(WSL1)或LxInitMessageCreateProcessUtilityVm(WSL2)消息,消息中携带:命令行、当前目录、环境变量、用户名等。两条路径的实现略有差异:

  • WSL1 路径:session leader 直接fork(),子进程在exec()进入用户 Linux 进程前,先配置好 uid/gid、当前目录以及 stdin/stdout/stderr 标准文件描述符;
  • WSL2 路径:session leaderfork()出 relay 进程,由 relay 负责创建用户进程并把输出中继回wsl.exe。

中继 Linux 进程的输入输出到 Windows

CreateLxProcess 返回的五个句柄

用户进程创建成功后,wslservice.exe从CreateLxProcess()返回给wsl.exe。在 WSL2 场景下,wsl.exe会收到以下 HANDLES(见 boot-process.md):

句柄用途
STDIN向 Linux 进程写入标准输入
STDOUT读取 Linux 进程的标准输出
STDERR读取 Linux 进程的标准错误
Control channel通知 Linux 进程终端变化(如wsl.exe终端窗口被调整大小时),并将变化应用到 Linux 进程
Interop channel双向用途,见下

relay:WSL2 的中继进程

WSL2 下真正承担中继工作的是 relay 进程。它由 session leader 在收到LxInitMessageCreateProcessUtilityVm消息后创建,随后与wslservice.exe建立多条 hvsocket 通道,分别用于:

  • 中继标准文件描述符(stdin / stdout / stderr);
  • 中继终端信息(如 Windows 侧窗口尺寸变化);
  • 向 Windows 通知 Linux 进程的退出。

通道就绪后,relayfork()成两个进程:父进程负责读写子进程的标准文件描述符并中继给 Windows;子进程调用exec()启动用户进程(本例中为/bin/bash)。随后wsl.exe与 relay 之间全双工中继 STDIN / STDOUT / STDERR——这正是你在终端里与 bash 交互的物理通道。

STDIN / STDOUT / STDERR 的中继逻辑

STDIN、STDOUT、STDERR三个句柄用于把 Linux 进程的输入输出中继到 Windows 终端。关键点在于:句柄类型不同(终端、管道、文件等),wsl.exe会套用不同的中继逻辑,以达成 Windows 与 Linux 之间最好的兼容性。这部分逻辑集中在 relay.cpp(Windows 侧中继实现;注意与 Linux 侧的 relay 进程区分)。

Control channel 与 Interop channel

Control channel用于把终端变化通知给 Linux 进程——典型场景是wsl.exe终端窗口被 resize 时,变化会经由该通道同步应用到 Linux 进程(对应 lxinitshared.h 中的LxInitMessageWindowSizeChanged消息)。

Interop channel有两种用途:

  1. 从 Linux 创建 Windows 进程——即 WSL 的双向互操作能力,详见 interop.md;
  2. 通知wsl.exeLinux 进程已退出——通过LxInitMessageExitStatus消息传递退出码。

进程退出与 wslhost.exe 接管

当 Linux 进程退出后,relay 通过waitpid()回收子进程,并向wsl.exe发送LxInitMessageExitStatus(进程退出码);wsl.exe收到后刷新剩余 IO,并以与 Linux 进程相同的退出码退出自身——这正是 WSL 命令的退出码能够无缝传递给 Windows 脚本的原因。

还有一个值得注意的边界场景:如果wsl.exe在 Linux 进程退出之前被终止(例如用户直接关闭了终端窗口),wslhost.exe 会接管 Interop channel,继续处理执行 Windows 进程的请求,避免正在运行的 Linux 进程因宿主进程消失而失去互操作能力。

附:关键源码路径速查

关注点仓库路径
消息与响应枚举(mini_init / init / gns 全部消息)src/shared/inc/lxinitshared.h
mini_init消息处理(LaunchInit / EarlyConfig / InitialConfig 等)src/linux/init/main.cpp
init的 session 与进程创建src/linux/init/init.cpp
网络配置(Linux 侧)src/linux/init/GnsEngine.cpp
发行版注册表查询src/windows/service/exe/DistributionRegistration.cpp
实例生命周期关联src/windows/service/exe/Lifetime.cpp
WSL2 虚拟机管理src/windows/service/exe/WslCoreVm.cpp
HCS JSON schemasrc/windows/common/hcs_schema.h
Windows 侧 IO / 终端中继逻辑src/windows/common/relay.cpp
COM 服务与会话src/windows/service/exe/LxssUserSession.cpp
WSL 架构总览doc/docs/technical-documentation/index.md

至此,从用户敲下wsl.exe到 bash 提示符出现的完整链路已经清晰:wsl.exe通过 COM 驱动wslservice.exe,后者借 HCS 拉起承载 mini_init 的虚拟机,mini_init 通过两级配置消息完成环境准备并派生 gns 与 init,init 创建 session leader,session leader 再经由 relay 把用户进程接入 Windows 的终端 IO。理解这条链路,是进一步研究 WSL 网络、互操作、systemd 集成与调试疑难问题的坚实基础。

【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询