在調試器里看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,位於system32lxss子目錄下。它使用svchost.exe作為宿主運行。
圖4 WSL的子系統服務DLL
運行在內核空間的Linux子系統驅動,有兩個,一個文件名叫lxss.sys,另一個叫LxCore.sys,都位於system32drivers目錄中,圖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後,便可以在當前用戶的AppDataLocallxss下看到Ubuntu的各個子目錄和文件了,圖7所示的便是根文件系統下的子目錄和文件,可以看到很多熟悉的名字:tmp、boot、etc、home、sbin、bin、usr、var等。
圖7 磁碟上的Ubuntu根目錄
安裝其他應用
UoW里已經包含了很多常用的命令和工具,包括著名的軟體包管理工具apt-get。所以很容易在UoW里安裝其它軟體包,只要使用以下這些命令就可以了。
對老雷來說,最想安裝的當然就是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進程,可以看到它是最小進程。
除了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處存放的便是進程的可執行文件路徑。基於此,便可以使用如下命令來顯示了:
如果有人問執行uname -a會返回什麼,試一下便知道了。
在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內核之上,這是件多麼激動人心的事啊。它具有劃時代的意義,開一代先河。它在封閉的視窗系統上打開一扇門,代表自由開發的小企鵝走了進去,有了新的樂園。它會激發很多想像,很多創新,當然也可能有很多邪念(你懂的,我在說病毒、勒索和安全)……
點擊展開全文


※RethinkDB聯合創始人分享的44條工程管理經驗
※微博近幾年的架構演進之路和架構師的技能素養
※深度學習在推薦領域的應用
TAG:CSDN |
※Undo 發布用於 Linux 調試的 Live Recorder 5.0
※Facebook 開源其調試平台 Sonar,支持 Android 與 iOS
※使用Visual Studio Code編譯、調試Apollo項目
※Eclipse Debug 調試
※linux性能調試之iostat
※基於vue2.0 +vuex+ element-ui後台管理系統:本地調試詳細步驟
※如何使用Ghostscript調試PostScript
※如何使用curl調試openstack的api
※教你使用Vue.js的DevTools來調試你的vue項目
※使用 Visual Studio Code 搭建 C/C+開發和調試環境
※喲,寫Bug呢?Facebook發布AI代碼調試工具SapFix
※Servlet 調試
※windbg藍屏調試
※如何在 Linux 或者 UNIX 下調試 Bash Shell 腳本
※如何直接在瀏覽器中查看和調試Django-Restful介面
※PyTorch代碼調試利器:自動print每行代碼的Tensor信息
※A 站受黑客攻擊,近千萬條用戶數據外泄;Facebook 開源其調試平台 Sonar,支持 Android與iOS
※Android遠程調試Web頁面
※NI推出InstrumentStudio軟體 簡化了自動化測試系統的開發和調試
※使用pdb進行Python調試