在调试器里看Windows 10的Linux子系统

Windows 10是微软第三代NT团队的力挽狂澜之作,大刀阔斧地改造革新,目标是重塑Windows(Reinvent Windows)!在众多新特征中,Linux环境子系统(WSL)无疑是最具开创性和最拉风的一个。

启用WSL

在2016年3月30日开幕的Build大会上,微软向广大开发者宣布Windows 10将支持Linux应用。在2016年4月7号推送的 Windows 10 build 14328 fast ring中首次包含了WSL。在Windows 10的周年更新(Anniversary Update)中包含了相对完整的Beta版本。

值得说明的是,不是所有的Windows 10系统都能运行WSL,要满足两个基本要求:64位和Build号不低于14393.0(笔者使用的两个版本分别是14393.1198和14393.479)。

在满足上述条件的Windows 10中,WSL也不是默认安装的,而是需要通过以下两个步骤来启用。

第一步是通过Settings -> Update and Security -> For developers打开开发者模式(参见图1)。 

图1 打开开发者模式

第二步是通过“Turn Windows features on or off”打开WSL功能(图2)。 

图2 打开WSL功能

点击图2所示的确定按钮后,Windows 10会安装WSL的组件,需要一点时间。安装完成后,要重启系统。重启后,在开始菜单区输入bash,如果看到图3所示的菜单项就代表WSL启用成功了。Bash是GNU旗下的命令处理器和外壳(shell)程序,包括Ubuntu在内的很多Linux发行版本和苹果macOS都使用它作为默认的控制台外壳和命令处理器。Windows 10中使用的就是Ubuntu版本的Bash。因此,WSL有时也被称为Bash on Ubuntu on Windows。

图3 Bash on Ubuntu on Windows

启用WSL成功代表着在Windows系统中搭建好了运行Linux应用的基本环境,这个环境就是所谓的Windows Subsystem for Linux(WSL)。环境子系统是NT内核固有的机制,目的是可以在一个NT内核运行不同类型的应用程序。在支持WSL之前,有三种子系统:Windows子系统、OS/2子系统和POSIX子系统。

每个子系统通常都有一个子系统服务进程和一个内核态驱动,对于Windows子系统,分别是著名的CSRSS.exe和Win32K.sys。对于WSL,它的组成主要有以下几个部分:

  • 以系统服务形式运行的子系统服务进程LxssManager。在服务管理器里,可以看到这个服务。服务的描述信息很详实,不妨引用一下:LXSS Manager服务支持运行本机ELF二进制文件。该服务提供在Windows上运行ELF二进制文件所需的基础结构。如果停止或禁用该服务,这些二进制文件将不再运行。LxssManager的核心代码是一个DLL,位于system32\lxss子目录下。它使用svchost.exe作为宿主运行。 

    图4 WSL的子系统服务DLL

  • 运行在内核空间的Linux子系统驱动,有两个,一个文件名叫lxss.sys,另一个叫LxCore.sys,都位于system32\drivers目录中,图5是lxss驱动在注册表中的安装选项。 

    图5 Linux子系统驱动的注册表表项

  • 用于与Windows环境接口的Bash启动(Bash Launcher)程序bash.exe,位于system32目录下,图3中的菜单项指向的就是这个程序。
  • 一个用于管理和维护WSL的工具程序,名为LxRun.exe。

安装Ubuntu

有了上面的基本运行环境就可以运行Linux应用程序了么?不是的,还需要安装Linux系统,包括系统程序、库文件和必要的工具程序。不过不用担心,微软已经和Canonical(Ubuntu背后的公司)合作准备好了一个特殊版本的Ubuntu,称为Ubuntu On Windows(以下简称UoW)。第一次启动Bash Launcher时,它就会提示安装UoW,如图6所示。

图6 安装Ubuntu On Windows

安装UoW后,便可以在当前用户的AppData\Local\lxss下看到Ubuntu的各个子目录和文件了,图7所示的便是根文件系统下的子目录和文件,可以看到很多熟悉的名字:tmp、boot、etc、home、sbin、bin、usr、var等。

图7 磁盘上的Ubuntu根目录

安装其它应用

UoW里已经包含了很多常用的命令和工具,包括著名的软件包管理工具apt-get。所以很容易在UoW里安装其它软件包,只要使用以下这些命令就可以了。

sudo apt-get update
sudo apt-get install packagename
sudo apt-get remove packagename
sudo apt-cache search word

对老雷来说,最想安装的当然就是GDB,这只要执行sudo apt-get install gdb就可以了,安装过程如图8所示。

图8 安装GDB

观察Linux实例的创建过程

做好以上准备工作后,我们的下一个目标就是在调试器里理解WSL的工作原理。首先我们要观察的是在WSL中启动Linux实例的过程。

在内核调试会话中对nt!MmInitializeProcessAddressSpace设置断点,然后启动bash.exe,第一次命中后直接放行(bash.exe是普通的Windows进程,不感兴趣),第二次命中时,看到的是services.exe在创建svchost.exe,切换到bash.exe,观察其栈回溯,可以看到它正在构建用以与WSL服务进程通信的SvcComm对象(图9)。此对象的构造函数内部会调用CoCreateInstance创建由WSL服务进程实现的进程外COM对象。这会触发创建COM对象的宿主进程,即svchost.exe。

图9 Bash Launcher进程在与WSL服务进程建立通信

图10是来自WSL团队官方博客的WSL组件协作图,左侧的bash.exe和Linux会话管理服务之间的黑色箭头代表它们之间是通过COM技术交互的。

图10 WSL组件协作图

恢复目标执行后,断点很快又命中,栈回溯如图11所示。

图11 创建Linux实例

从栈帧12-17可以看出,这次命中断点的正是WSL服务进程,它在通过I/O控制(I/O Control)方式与内核空间的LxCore通信。栈帧9-b中的Adss代表的是Android subsystem,是微软放弃了的Astoria项目(用于在Windows上运行Android应用程序)留下的痕迹。

根据笔者的分析和图10、图11所示栈回溯是在创建Linux系统中的init进程。当init进程启动后,会创建真正的bash进程。图12显示了创建bash进程后的WSL有关进程列表。

图12 创建bash进程后的WSL有关进程列表

图12中一共有四个进程,第一个是bash launcher进程,从操作系统的角度看,它是典型的Windows程序。第二个进程是WSL的子系统服务进程,第三个是它发起创建的init进程,第四个是init进程创建的bash进程。通过ParentCid(父进程ID)字段,可以看到后三个进程有父子关系。图13是使用Process Explorer观察的结果。

图13 WSL的进程关系图

最小进程和Pico进程

要深入理解WSL,必须先了解最近几年里NT内核引入的两个新概念:最小进程和Pico进程。最小进程是Windows 8.1引入的,代表一个最小化的进程对象,它具有名字、令牌、保护级别等基本属性,但是进程空间是空的,没有PEB,也没有NTDLL,也没有句柄表。EPROCESS结构体中的Minimal为1代表该进程是最小进程。比如观察图12中的init进程,可以看到它是最小进程。

kd> dt _EPROCESS ffffce8d779b9780 -y Minimal
nt!_EPROCESS
   +0x6c4 Minimal : 0y1

除了WSL使用最小进程外,Windows 10中的内存压缩进程也是最小进程。

Pico进程是最小进程的一种,它的特点是有一个驱动程序与其关联,当这个进程内发生系统调用时,内核会把系统调用转给这个驱动,这个驱动被称为Pico Provider,这样的进程被称为Pico进程。图14是来自WSL官方博客的插图,用以说明NT系统中的三种进程。

图14 三种进程

Pico的意思是微小,常常出现在容器技术中。从容器技术的角度来看,可以把Pico进程看作一个沙盒。事实上,Pico进程便源于Stony Brook University、Cambridge和微软研究院联合开发的Drawbridge项目。在Channel9上可以找到一个名为Drawbridge: A New Form of Virtualization for Application Sandboxing的录像,介绍了Library OS的思想和Drawbridge项目实现的技术原型。

用于WSL的Pico进程空间中可以执行Linux的原生程序,当发生系统调用时,内核会将这些调用转发给与它关联的驱动(Pico Provider)。比如图15所示的栈回溯显示的便是NT内核将系统调用转发给LxCore的过程。

图15 转发系统调用

文章收尾之际再介绍一个调试小技巧。对于已经有20多年历史的NT内核来说,WSL绝对是新生事物,很多配套设施还不完善,比如在WinDBG中列进程时所有Pico进程的名字都显示为System Process(图12中下面两个),很不方便。如何知道它们的Linux进程名呢?是有办法的。在EPROCESS中有个名为PicoContext的字段(目前偏移0x708),指向的是一个记录Pico属性的结构体,它的详细定义没有公开,笔者通过反汇编了解到在它的偏移0x180处存放的便是进程的可执行文件路径。基于此,便可以使用如下命令来显示了:

kd> dU poi(poi(ffffce8d779b9780+708)+180)
ffffa782`0e43d960  "/init"
kd> dU poi(poi(ffffce8d78f31080+708)+180)
ffffa782`19840fa0  "/bin/bash"

如果有人问执行uname -a会返回什么,试一下便知道了。

gedu@DESKTOP-4NBEECU:/mnt/c/Windows/System32$ uname -a
Linux DESKTOP-4NBEECU 3.4.0+ #1 PREEMPT Thu Aug 1 17:06:05 CST 2013 x86_64 x86_64 x86_64 GNU/Linux

在LXCORE!LxpSyscall_NEWUNAME设个断点便可以跟踪这个结果的产生过程,有些来自字符串常量,有些是动态拼接的。预知其详,笔者强烈建议读者亲自动手试一下。

回望历史,微软一度对Linux充满恐惧和仇恨,想趁其未壮而屠之于襁褓。2002年9月,被称为视窗之父的微软高管Jim Allchin在与很多列客户交流和调查后,写了封很长的邮件给微软的另一些高管,其中有这样一段板面

My conclusion: We are net on a path to win against Linux We must change some things and we must do it immediately. The current white papers, etc. are too high level and they are not going to cut it, Here are specific actions that I have concluded that we must take.

接下来详细部署了一系列任务……

但事实上,Linux一天天壮大了。那只好成为盟友吧,一起拥抱新的时代,一起干杯(背后也可能骂娘)!

无论如何,一个新的时代开始了,二进制的Linux程序可以以原生形式直接运行在NT内核之上,这是件多么激动人心的事啊。它具有划时代的意义,开一代先河。它在封闭的视窗系统上打开一扇门,代表自由开发的小企鹅走了进去,有了新的乐园。它会激发很多想象,很多创新,当然也可能有很多邪念(你懂的,我在说病毒、勒索和安全)……

时间: 2024-12-18 18:13:14

在调试器里看Windows 10的Linux子系统的相关文章

Windows下的Linux子系统安装,WSL 2下配置docker

Windows下的Linux子系统安装,WSL 2下配置docker 前提条件: 安装WSL 2需要Windows 10版本是Build 18917或更高,首先先确认系统版本已升级. 在“启用或关闭Windows功能”中启用以下两个选项:Windows虚拟机平台.适用于Linux的Windows子系统. 启用这些更改后,重新启动计算机. 从如上图中安装最新的Ubuntu发行版. 1. Powershell下配置 将WSL2作为Ubuntu的默认架构 wsl -- set - version Ub

Windows 10运行Ubuntu子系统

Bash 是 Linux/Unix 上非常流行的命令行 Shell,它是 Ubuntu.RHEL 等 Linux 发行版以及苹果 OS X 操作系统默认的命令行 Shell. Bash on Ubuntu on Windows 是通过 Windows Subsystem for Linux(WSL)这一 Windows 10 的最新特性实现的,使用此功能,你可以在 Windows 中原生运行 Linux 的大多数命令行程序. 启用 WSL 功能之后,如果使用 Bash,则 Windows 10

【ASP.NET Core】在Win 10 的 Linux 子系统中安装 .NET Core

在上一篇文章中,老周扯了一下在 Windows 10 中开启 Linux 子系统,并且进行了一些简单的设置.本篇咱们就往上面安装 .net core . 老周假设你从来没有用过 Linux,所以,接着上一次的博文,老周先给您介绍几个可能比较常用的东东. 切换到 root 用户上下文 当你启动 Ubuntu 子系统后,你会看到,在你的用户名.机器名.路径名后有个 $ 符号.如下图. 这表示当前用户的权限是受到限制的,如果想要执行某些需要高权限的命令(这个类似于 Windows 中的以管理员身份运行

windows 下使用Linux 子系统-安装.net core 环境

在 Windows 上进行 web 开发,比较普遍的方案是使用 phpstudy 或者别的一些集成环境软件进行环境搭建,写好代码后将代码上传至版本管理工具 git/svn,再将代码同步到 Linux 服务器,这个过程当中开发者的开发环境(Windows)与代码最终执行的环境(Linux)不一致经常会导致一些奇奇怪怪的问题,想在 Windows 上进行 linux 下的 web 开发,不想用 mac(毕竟没钱买 mac),又不想使用虚拟机(虚拟机开机速度慢,添加站点需要重启,分配内存会导致机器变得

Windows中安装Linux子系统的详细步骤

早就听说Windows中可以安装Linux子系统,体验了一下,感觉还是不错的,下面直接开始安装和配置步骤吧! 开启Windows中的配置 首先开启开发者模式 打开"所有设置"进入"更新和安全" 选择左侧的"开发者选项",然后选择"开发者模式" 然后打开"适用于Linux的Windows子系统" 在控制面板中打开"程序和功能"窗口 点击左侧"启用或关闭Windows功能"

将Chrome调试器里的JavaScript变量保存成本地JSON文件

我写了一个系列的文章,主要用来搜集一些供程序员使用的小工具,小技巧,帮助大家提高工作效率. 推荐一个功能强大的文件搜索工具SearchMyFiles 介绍一个好用的免费流程图和UML绘制软件-Diagram Designer 介绍Windows任务管理器的替代者-Process Explorer 介绍一个强大的磁盘空间检测工具Space Sniffer 如何在电脑上比较两个相似文件的差异 程序员工作效率提升系列-推荐一个JSON文件查看和修改的小工具 我们在Chrome开发者工具的Console

宇宙最强调试器DDD(Data Display Debugger)

Linux主流调试器是gdb,但它是纯命令行界面的,调试起来不方便,我需要更强大的力量.在试用了各种工具之后,我相信我找到了,是的没错,就是它--宇宙最强调试器--DDD. DDD介绍 DDD全称Data Display Debugger,当我第一次见到它时,它的界面着实让我吃了一惊,如此的简陋,如此的怪异,我甚至想立刻删了它,但是当我见识到它强大的功能时,我被深深的震撼了,如此的飘逸,如此的不羁,我的脑海中突然想到了一个词来形容它--犀利! 没错,就是这么犀利,它是gdb的最优图形化前端,它继

调试器如何工作(2)

调试器如何工作:第二部分--断点 原作者:Eli Bendersky http://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints 这是关于调试器如何工作系列文章的第二部分.在这之前确保你读过第一部分. 在这部分 我将展示在调试器中如何实现断点.断点是调试的两大支柱之一--另一个是能够在被调试进程内存里查看值.在第一部分里我们已经预览过另一个支柱,但断点仍然笼罩在神秘的面纱下.看完本文,它们不再是了.

安装 Linux 与 Windows 10 双系统,你需要了解的一切

该选Windows 10还是Linux Mint?鱼与熊掌当然可以兼得,但咱们得掌握点小技巧才能顺利搞定. Windows 10绝不是唯一一款值得我们安装在自己计算机之上的免费操作系统.Linux只靠一块U盘就能顺利运行,而且完全无需对现有系统作出任何修改.当然,如果大家打算定期加以使用,最好也能在电脑上给Linux留出一席之地. 同时安装Linux发行版与Windows也就是大家常常提到的“双系统”方案,大家可以在每一次启动PC设备时选择自己要使用哪款操作系统.对于大多数人来说,这是安装Lin