本章將介紹 Windows 的基本概念,這些概念是方便接下來對 Windows 內(nèi)部理解從而提前了解關(guān)于 Windows 基本概念和部分術(shù)語。
Windows API
Windows 應(yīng)用程序編程接口(application programming interface,API)是Windows操作系統(tǒng)家族的用戶模式系統(tǒng)編程接口。64 位 Windows 發(fā)布前,32 位 Windows 的編程接口被稱為 Win32 API。這主要是為了將其與早期的 16 位 Windows API 區(qū)分開來?,F(xiàn)在大部分稱呼 Windows API 為 Win32 API 同時指代 32 位和 64 位 Windows 的編程接口。
Windows API 的風(fēng)格
Windows API 最初只包含 C 語言風(fēng)格的函數(shù)。目前,已有數(shù)千種此類函數(shù)可供開發(fā)者使用。在 Windows 誕生之日,C 語言是最自然的選擇,因?yàn)樗梢哉f是眾多語言的最小公分母(也就是說,也可以通過其他語言來訪問 C 語言),并且 C 語言足夠底層,足以用來暴露操作系統(tǒng)服務(wù)。但 C 語言的不足之處是函數(shù)的絕對數(shù)量少以及缺少命名一致性和邏輯分組(例如 C++ 的命名空間)。這些難題造成的后果之一便是一些較新的 API 使用了不同的 API 機(jī)制:組件對象模型(component object model,COM)。
COM 最初是為了讓 Microsoft office 應(yīng)用程序能夠在文檔之間通信并交換數(shù)據(jù)(例如將 Excel 表格嵌入 Word 文檔或者 PowerPoint 演示文稿)。這種能力也叫做對象鏈接和嵌入(object Linking and embedding,OLE)。OLE 最初使用一種古老的 Windows 消息傳遞機(jī)制 —— 動態(tài)數(shù)據(jù)交換(dynamic data exchange,DDE)實(shí)現(xiàn)。DDE 有一些固有局限,因此人們開發(fā)了新的通信方式:COM。實(shí)際上 COM 最初被稱為 OLE2,大概在 1993 年正式發(fā)布。
COM 基于兩個基本原則:
- 第一個原則:客戶端可以通過接口與對象(有時也稱為 COM 服務(wù)器對象)通信。接口是一種明確定義的“合約”,由一系列在邏輯上互相關(guān)聯(lián)的方法組成,并按照虛擬表調(diào)度機(jī)制分組。這也是一種 C++ 編譯器實(shí)現(xiàn)虛擬表調(diào)度的通用方法。借此可以實(shí)現(xiàn)二進(jìn)制兼容性,避免編譯器名稱重整(mangling)問題,并可以通過很多語言(和編譯器)調(diào)研這些方法,例如 C、C++、visual basic、.net、delphi 等。
- 第二個原則:組件的實(shí)現(xiàn)可以動態(tài)加載,無須靜態(tài)鏈接到客戶端。
COM 服務(wù)器這個稱呼通常代表用于實(shí)現(xiàn) COM 類的動態(tài)鏈接庫(dynamic link library,DLL)或可執(zhí)行文件(executable,EXE)。COM 還提供了與安全性、跨進(jìn)程排列(marshalling)、線程模型等有關(guān)的重要功能。對 COM 的詳細(xì)介紹這里就不詳細(xì)說明了,大家感興趣可以參考 Don Box 撰寫的 Essential COM 一書。
Windows 運(yùn)行時
Windows 8 新增了一種名為 Windows 運(yùn)行時的全新 API 和支持運(yùn)行時(有時也簡稱為 WinRT,但是和基于 ARM 架構(gòu)的 Windows 操作系統(tǒng)版本—— Windows RT 是兩回事)。Windows 運(yùn)行時由平臺服務(wù)組成,主要面向 Windows 應(yīng)用(曾被稱為 metro 應(yīng)用、modern 應(yīng)用、immersive 應(yīng)用以及 Windows 商店 APP)的應(yīng)用開發(fā)者。Windows 應(yīng)用可運(yùn)行在不同類型和規(guī)格的設(shè)備上,從小型的物聯(lián)網(wǎng)設(shè)備到手機(jī)、平板電腦、筆記本電腦、臺式機(jī),甚至 Xbox one 和 HoloLens 等設(shè)備均可支持。
從 API 的角度來看,WinRT 是在 COM 的基礎(chǔ)上構(gòu)建出來的,并對基本的 COM 基礎(chǔ)架構(gòu)增加了各種擴(kuò)展。例如,WinRT 可使用完整的類型元數(shù)據(jù)(存儲在 WINMD 文件中,基于 .NET 元數(shù)據(jù)格式)在 COM 中擴(kuò)展出一種名為類型庫的類似概念。從 API 設(shè)計(jì)的角度來看,它比經(jīng)典 Windows API 函數(shù)更內(nèi)斂,可提供命名空間、層次結(jié)構(gòu)、一致的命名以及編程模式。
Windows 應(yīng)用沿襲了一系列新規(guī)則,這一點(diǎn)和普通的 Windows 應(yīng)用程序(現(xiàn)在可稱為 Windows 桌面應(yīng)用程序或者經(jīng)典 Windows 應(yīng)用程序)截然不同。
各種 API 與應(yīng)用程序間的關(guān)系并不那么直白。桌面應(yīng)用可以使用 WinRT API 的子集,而 Windows 應(yīng)用也可以使用 Win32 和 COM API 的子集。這部分的具體細(xì)節(jié)可以參考 MSDN 文檔,WinRT API 并不是新增的“原生” API,而有點(diǎn)類似使用傳統(tǒng)的 Windows API 的 .NET。
.NET Framework
.NET Framework 是 Windows 的一部分。下表列出了 Windows 默認(rèn)安裝的 .NET Framework 版本。新版 .NET Framework 也可以安裝在舊版本的 Windows 操作系統(tǒng)中。
Windows 版本 | .NET Framework 版本 |
---|---|
Windows 8 | 4.5 |
Windows 8.1 | 4.5.1 |
Windows 10 | 4.6 |
Windows 10 版本 1511 | 4.6.1 |
Windows 10 版本 1607 | 4.6.2 |
.NET Framework 包含兩個主要組件:
- 公共語言運(yùn)行時(common language runtime,CLR)。這是 .NET 的運(yùn)行時引擎,其中包含的即時(just lm time, JIT)編譯器可以將公共中間語言(common intermediate language, CIL)指令轉(zhuǎn)換為底層硬件 CPU 機(jī)器語音、垃圾回收器、類型驗(yàn)證、代碼訪問安全性等內(nèi)容。它是作為一種 COM 進(jìn)程內(nèi)服務(wù)器(DLL)實(shí)現(xiàn)的,可使用 Windows API 提供的各類設(shè)施。
- .NET Framework 類庫(framework class library, FCL)。這是一個龐大的類型集合,用于實(shí)現(xiàn)客戶端和服務(wù)器應(yīng)用程序通常可能需要的功能,例如用戶界面服務(wù)、網(wǎng)絡(luò)、數(shù)據(jù)庫訪問等。
通過提供上述功能以及新的高級編程語言(C#、visual basic、F#)和支持工具,.NET Framework 可以幫助開發(fā)者提升目標(biāo)應(yīng)用程序的開發(fā)銷量并提高安全性和可靠性。.NET Framework 和 Windows 操作系統(tǒng)直接的關(guān)系如下圖所示:
服務(wù)、函數(shù)和例程
在 Windows 用戶文檔和編程文檔中,很多術(shù)語在不同語境下有著不同的含義。例如,“服務(wù)” 這個詞可以代表操作系統(tǒng)中可調(diào)用的例程、設(shè)備驅(qū)動程序,也可以代表某個服務(wù)器進(jìn)程。下面列出了不同術(shù)語的含義:
- Windows API 函數(shù)。Windows API 中已公開并且可以調(diào)用的子例程。如 CreateProcess、CreateFile、GetMessage。
- 原生系統(tǒng)服務(wù)(或系統(tǒng)調(diào)用)。操作系統(tǒng)中未公開,但可以從用戶模式調(diào)用的底層服務(wù)。例如:Windows 的 CreateProcess 函數(shù)調(diào)用 NtCreateUserProcess 這個內(nèi)部系統(tǒng)服務(wù)可新建一個進(jìn)程。
- 內(nèi)核支持函數(shù)(或例程)。在 Windows 操作系統(tǒng)內(nèi)部,只能從內(nèi)核模式調(diào)用的子例程。例如:驅(qū)動程序可以調(diào)用 ExAllocatePoolWithTag 例程從 Windows 系統(tǒng)堆中分配內(nèi)存。
- Windows 服務(wù)。由 Windows 服務(wù)控制管理器啟動的進(jìn)程。例如,運(yùn)行在用戶模式中的 Task Scheduler 服務(wù)也可以支持 schtasks 命令
- 動態(tài)鏈接庫(DLL)??烧{(diào)用的子例程互相鏈接成的二進(jìn)制文件,使用該子例程的應(yīng)用程序可以動態(tài)加載這樣的文件。例如,Msvcrt.dll 和 Kernel32.dll(Windows API 子系統(tǒng)庫之一)。Windows 用戶模式組件和應(yīng)用程序大量使用了 DLL。相比靜態(tài)庫,DLL 的優(yōu)勢在于應(yīng)用程序可以共享 DLL,Windows 可確保多個應(yīng)用程序使用的同一個 DLL 只在內(nèi)存中存在一個副本。
進(jìn)程
雖然表面上程序和進(jìn)程看起來很像,但是卻存在本質(zhì)差異。程序是一種靜態(tài)指令序列,而進(jìn)程是一種容器,其中包含了執(zhí)行程序?qū)嵗龝r會用到的一系列資源。從最高層的抽象來看,一個 Windows 進(jìn)程可包含下列元素:
- 一塊私有的虛擬地址空間。可供該進(jìn)程使用的一系列虛擬內(nèi)存地址。
- 一個可執(zhí)行的程序。定義了初始代碼和數(shù)據(jù),會映射到進(jìn)程的虛擬地址空間。
- 一個已打開句柄的列表。句柄會映射到各種系統(tǒng) 資源。例如,信號量、同步對象以及可被進(jìn)程中所有線程訪問的文件。
- 一個安全上下文。用于確定與進(jìn)程相關(guān)的用戶、安全組、特權(quán)、屬性、聲明、能力、用戶賬戶控制(user account control,UAC)虛擬化狀態(tài)、會話、受限用戶賬戶狀態(tài)身份的訪問令牌,此外還包含 appcontainer 標(biāo)識符和相關(guān)沙箱信息。
- 一個進(jìn)程 ID。唯一標(biāo)識符,從內(nèi)部來說屬于客戶端 ID 標(biāo)識符的一部分。
- 至少一個執(zhí)行線程。雖然可以創(chuàng)建“空的”進(jìn)程。
很多工具能幫我們查看(甚至修改)進(jìn)程和進(jìn)程信息。下面我們會來嘗試演示一下。
使用任務(wù)管理器查看進(jìn)程信息
在 Windows 下可以使用快捷鍵 Ctrl+shift+esc 打開任務(wù)管理器,或者通過在工具欄右鍵鼠標(biāo)選擇打開任務(wù)管理器。
在進(jìn)程選項(xiàng)卡中默認(rèn)會顯示出四列信息:CPU、memory、disk 和 network。右鍵點(diǎn)擊表頭后可以選擇顯示更多了列信息或者隱藏部分顯示的信息??娠@示的信息包括進(jìn)程名稱、進(jìn)程 ID、類型、狀態(tài)、發(fā)布者以及命令行。一些進(jìn)程可以展開,展開后顯示該進(jìn)程創(chuàng)建的頂級可見窗口。
可以進(jìn)入詳細(xì)信息選項(xiàng)卡,在這里也會顯示出進(jìn)程信息,但會以更緊湊的方式顯示。這里不會顯示進(jìn)程創(chuàng)建的窗口,但是會提供更多不同類型的信息列。
同樣,在詳細(xì)信息里也可以右鍵點(diǎn)擊表頭,選擇設(shè)置列信息從而能夠顯示出更多關(guān)于進(jìn)程的信息。
這里需要重點(diǎn)說明幾個選項(xiàng)值的作用:
- “線程”: 顯示出每個進(jìn)程所擁有的線程數(shù)量。
- “句柄”:顯示出該進(jìn)程內(nèi)部鎖打開的內(nèi)核對象句柄數(shù)量。
- “狀態(tài)”:顯示該進(jìn)程當(dāng)前的運(yùn)行狀態(tài),running、suspended 等情況。
父進(jìn)程
每個進(jìn)程還會指向自己的父進(jìn)程(父進(jìn)程可以是創(chuàng)建者進(jìn)程,但也并非總是如此)。如果父進(jìn)程已經(jīng)不存在,這些信息將不再更新。因此進(jìn)程有可能指向不存在的父進(jìn)程。但這并不會造成問題,因?yàn)槿魏芜M(jìn)程的運(yùn)行都不依賴父進(jìn)程信息的有效與否。process explorer 會考慮父進(jìn)程的啟動時間,以避免子進(jìn)程附加到重用的進(jìn)程 ID 上。
大部分工具都不會顯示進(jìn)程的父進(jìn)程或創(chuàng)建者進(jìn)程的 ID 這個屬性。我們可以使用性能監(jiān)視器查詢 creating process ID 來獲取這些信息。通過使用 Windows 調(diào)試工具中的 Tlist.exe 工具并配合 /t 開關(guān)來顯示進(jìn)程樹。(在新的 Windows 10 中 Tlist.exe 已經(jīng)更改為 TaskList (Tasklist.exe),并且也不再支持 .t 選項(xiàng)了)
這里我們使用 MS 提供的 sysinternals 包中的 process explorer 來顯示更詳細(xì)的進(jìn)程信息。(這個工具的說明及下載鏈接:https://learn.microsoft.com/zh-cn/sysinternals/downloads/)
sysinternals 的 process explorer 可以顯示比其他類似工具更詳細(xì)的進(jìn)程和線程信息,此外還能顯示或?qū)崿F(xiàn)一些獨(dú)特的功能:
- 進(jìn)程安全令牌,例如租和特權(quán)列表以及虛擬化狀態(tài)。
- 通過高亮強(qiáng)調(diào)顯示進(jìn)程、線程、DLL 和句柄列表中的變化。
- 服務(wù)承載進(jìn)程內(nèi)的服務(wù)列表,包括服務(wù)的顯示名和描述。
- 其他進(jìn)程屬性列表,例如緩解策略和進(jìn)程保護(hù)級別。
- 包含在作業(yè)中的進(jìn)程以及作業(yè)細(xì)節(jié)。
- 承載 .NET 應(yīng)用程序的進(jìn)程以及與 .NET 相關(guān)的細(xì)節(jié),如 appdomain 列表,加載的程序集,以及 CLR 性能計(jì)數(shù)器。
- 承載 Windows 運(yùn)行時的進(jìn)程(沉浸式進(jìn)程)。
- 進(jìn)程和線程的啟動時間。
- 內(nèi)存映射文件的完整列表(不僅僅是 DLL)。
- 掛起進(jìn)程或線程的能力。
- 終止特定線程的能力。
- 輕松辨別一段時間內(nèi) CPU 資源消耗量最大的進(jìn)程。
注意:性能監(jiān)視器可以顯示一組指定進(jìn)程的 CPU 利用率,但是無法自動顯示性能監(jiān)視器會話啟動后創(chuàng)建的進(jìn)程信息,只有通過手動方式創(chuàng)建的二進(jìn)制輸出格式才能包含這樣的信息。
Process Explorer 還可以幫助用戶在一個位置輕松訪問下列信息:
- 進(jìn)程樹,并能將樹的部分內(nèi)容折疊。
- 進(jìn)程中打開的句柄,包括未命名的句柄。
- 進(jìn)程中的 DLL 列表。
- 進(jìn)程中的線程活動。
- 用戶模式和內(nèi)核模式的線程棧,包括使用 Windows 調(diào)試工具提供的 Dbghelp.dll 將地址映射到的名稱。
- 內(nèi)存管理器詳細(xì)信息,例如內(nèi)存提交量峰值、內(nèi)核內(nèi)存換頁限制,以及非換頁內(nèi)存池限制。
線程
線程是位于進(jìn)程中、供 Windows 調(diào)度執(zhí)行的一種實(shí)體。如果沒有線程,進(jìn)程的程序?qū)o法運(yùn)行。
線程包含以下基本要素:
- 代表進(jìn)程狀態(tài)的一系列 CPU 寄存器內(nèi)容。
- 兩個棧:一個供線程在內(nèi)核模式下執(zhí)行使用;另一個供線程在用戶模式下執(zhí)行使用。
- 一個供子系統(tǒng)、運(yùn)行時庫以及 DLL 使用,名為線程本地存儲的私有存儲區(qū)域。(thread-local storage,TLS)
- 一個名為線程 ID 的唯一標(biāo)識符。
此外,線程有時也會有自己的安全上下文,也成為令牌,主要被多線程服務(wù)器應(yīng)用程序用于模仿所服務(wù)的客戶端的安全上下文。
易失和非易失的寄存器以及私有存儲區(qū)域組合在一起形成了線程的上下文。由于這些信息在不同架構(gòu)的計(jì)算機(jī)上運(yùn)行的 Windows 中都是不同的。因此從本質(zhì)上來說,這種結(jié)構(gòu)和特定架構(gòu)相關(guān)的。Windows 的 GetThreadContext 函數(shù)可供我們訪問這種與架構(gòu)有關(guān)的信息(也稱為 CONTEXT 塊)。此外,每個線程還有自己的棧(由線程上下文中的棧寄存器部分指向)。
將執(zhí)行過程從一個線程切換到另一個線程需要內(nèi)核調(diào)度器的參與,這可能是一個高開銷的操作,尤其是在兩個線程需要頻繁互相切換時,為了減少開銷,Windows 實(shí)現(xiàn)了兩種機(jī)制:
- 纖程(fiber)
- 用戶模式調(diào)度(user-mode scheduling,UMS)線程
纖程
纖程可以讓應(yīng)用程序不借助 Windows 內(nèi)置的基于優(yōu)先級的調(diào)度機(jī)制直接安排自己的線程的執(zhí)行。纖程通常也可以叫做輕量級線程(lightweight thread)。在調(diào)度方面,纖程對內(nèi)核是不可見的,因?yàn)槔w程是通過 Kernel32.dll 在用戶模式下實(shí)現(xiàn)的。若要使用纖程,首先需要調(diào)用 Windows 的 ConvertThreadToFiber 函數(shù),該函數(shù)會將線程轉(zhuǎn)換為運(yùn)行中的纖程。隨后新轉(zhuǎn)換的纖程可通過 CreateFiber 函數(shù)創(chuàng)建更多纖程。(每個纖程可以有自己的一組纖程)但是與線程不同,纖程只有在調(diào)用 SwitchToFiber 函數(shù)并手動選擇之后才能開始執(zhí)行。新建的纖程將持續(xù)運(yùn)行,直到退出或再次調(diào)用 SwitchToFiber 函數(shù)并選擇運(yùn)行其他纖程。有關(guān)纖程函數(shù)的詳情可參閱 Windows SDK 文檔。
用戶模式調(diào)度線程
用戶模式調(diào)度(UMS)線程僅適用于 64 位 Windows,提供了與纖程類似的基本用途,但避免了纖程的大部分不足之處。UMS 線程有自己的內(nèi)核線程狀態(tài),因此對內(nèi)核可見, 借此多個 UMS 線程即可發(fā)出阻塞的系統(tǒng)調(diào)用,并可共享或競爭資源。或者,當(dāng)兩個或更多 UMS 線程需要在用戶模式下執(zhí)行操作時,還可以定期切換執(zhí)行上下文(由一個線程將執(zhí)行權(quán)讓給另一個線程),這個過程可在用戶模式下進(jìn)行,無須調(diào)度器參與。從內(nèi)核的角度來看,此時依然運(yùn)行了相同的內(nèi)核線程,沒有任何變化。當(dāng) UMS 線程執(zhí)行的操作需要進(jìn)入內(nèi)核(例如系統(tǒng)調(diào)用)時,可以切換到它自己的專屬內(nèi)核模式線程(這一過程叫做定向上下文切換,directed context Switch)。雖然并發(fā)的 UMS 線程依然無法通過多個進(jìn)程運(yùn)行,但它們符合一種預(yù)搶占(pre-emptible)模式,所以并不是完全合作的關(guān)系。
雖然線程有自己的執(zhí)行上下文,甚至一個進(jìn)程中的每個線程都共享了該進(jìn)程的虛擬地址空間,但一個進(jìn)程中的所有線程都可以完全讀寫訪問該進(jìn)程的虛擬地址空間。不過線程無法無意中引用到其他進(jìn)程的地址空間,除非其他進(jìn)程將自己的部分私有地址空間編程共享內(nèi)存區(qū)(在 Windows API 中稱為文件映射對象),或者除非一個進(jìn)程有權(quán)打開另一個進(jìn)程以使用跨進(jìn)程內(nèi)存函數(shù),例如 ReadProcessMemory 和 WriteProcessMemory 函數(shù)(此時進(jìn)程必須運(yùn)行在同一用戶賬戶下,沒有位于 AppContainer 或其他類型的沙箱中,并且除非目標(biāo)進(jìn)程有某種保護(hù)機(jī)制,否則默認(rèn)即可訪問)。
除了私有地址空間和一個或多個線程,每個進(jìn)程還有自己的安全上下文,以及到文件、共享內(nèi)存區(qū)、互斥體、事件、信號量等同步對象等內(nèi)核對象的打開的句柄列表。
圖中,VAD 指的是虛擬地描述符,是一種數(shù)據(jù)結(jié)構(gòu),內(nèi)存管理器用它來追蹤進(jìn)程使用的虛擬地址。
每個進(jìn)程的安全上下文存儲在一個名為訪問令牌的對象中。進(jìn)程訪問令牌包含 了進(jìn)程的安全標(biāo)識和憑據(jù)。默認(rèn)情況下,線程沒有自己的訪問令牌,但可以獲取令牌以便讓自己模擬另一個進(jìn)程(包括遠(yuǎn)程 Windows 系統(tǒng)中的進(jìn)程)的安全上下文,這一過程不會影響進(jìn)程中的其他線程(進(jìn)程和線程安全性的相關(guān)內(nèi)容后面還會詳細(xì)說明)。
作業(yè)
Windows 為進(jìn)程模型提供了一種名為“作業(yè)”的擴(kuò)展。作業(yè)對象的主要功能是將一組資源作為整體進(jìn)行管理和操作。作業(yè)對象可用于控制某些屬性,并對作業(yè)所關(guān)聯(lián)的一個或多個進(jìn)程加以限制。此外還可以為作業(yè)關(guān)聯(lián)的所有進(jìn)程,以及作業(yè)所關(guān)聯(lián)但關(guān)聯(lián)之后已經(jīng)終止的所有進(jìn)程記錄基本的賬戶信息。在一定程度上,作業(yè)對象彌補(bǔ)了 Windows 在結(jié)構(gòu)化進(jìn)程樹上的不足,同時很多時候它比 Unix 那樣的進(jìn)程樹更強(qiáng)大。
虛擬內(nèi)存
Windows 實(shí)現(xiàn)了一種基于平面(線性)地址空間的虛擬內(nèi)存系統(tǒng),讓每個進(jìn)程可以“覺得”自己能夠獲得一個極大地私有地址空間。虛擬內(nèi)存為內(nèi)存提供的邏輯視圖肯呢個為物理布局并不一致。在運(yùn)行時,內(nèi)存管理器可以(在硬件的協(xié)助下)對虛擬地址進(jìn)行轉(zhuǎn)換。即時映射到實(shí)際存儲了數(shù)據(jù)的物理地址,通過對保護(hù)和映射過程加以控制,操作系統(tǒng)即可確保進(jìn)程之間不會互相影響,也不會覆寫操作系統(tǒng)的數(shù)據(jù)。
由于大部分 OS 的物理內(nèi)存數(shù)遠(yuǎn)少于進(jìn)程運(yùn)行時所需的虛擬內(nèi)存總數(shù),內(nèi)存管理器需要對一些內(nèi)存內(nèi)容進(jìn)行轉(zhuǎn)換,即分頁到磁盤上。將數(shù)據(jù)分頁到磁盤上可以將物理內(nèi)存釋放給其他進(jìn)程或操作系統(tǒng)自身使用。當(dāng)線程需要訪問被分頁到磁盤上的虛擬地址時,虛擬內(nèi)存管理器會將相關(guān)信息從磁盤重新加載到物理內(nèi)存中。
應(yīng)用程序無須專門調(diào)整即可利用分頁功能所提供的好處,因?yàn)橛布闹С质沟脙?nèi)存管理器可以在進(jìn)程或線程不知道或者無須協(xié)助的情況下分頁。在兩個進(jìn)程所使用的虛擬內(nèi)存中,部分依然映射在物理內(nèi)存(physical memory RAM)中,另一部分則已經(jīng)被分頁到磁盤上。注意,連續(xù)的虛擬內(nèi)存塊可能被映射到不連續(xù)的物理內(nèi)存塊。這些塊也叫做頁,每個頁的默認(rèn)大小為 4KB。
每種硬件平臺的虛擬內(nèi)存地址空間大小各異。在 32 位 X86 系統(tǒng)中,虛擬內(nèi)存空間總數(shù)的理論最大值為 4GB。默認(rèn)情況下,Windows 會將這一地址空間中較低的一半(從 0x00000000 到 0x7FFFFFFF)分配給進(jìn)程,作為進(jìn)程獨(dú)有的私有存儲,并將較高的一半(從 0x80000000 到 0xFFFFFFFF)分配給自己作為受保護(hù)的操作系統(tǒng)內(nèi)存使用。較低一半的映射會通過變化體現(xiàn)當(dāng)前執(zhí)行進(jìn)程的虛擬地址空間,但較高一半的(大部分)映射總是由操作系統(tǒng)的虛擬內(nèi)存組成的。Windows 支持的啟動選項(xiàng),例如引導(dǎo)配置數(shù)據(jù)庫中的 increaseuserva 修飾符可以讓運(yùn)行帶有特殊標(biāo)記程序的進(jìn)程最多使用 3GB 的私有地址空間,僅為操作系統(tǒng)保留 1GB,該方式可以將數(shù)據(jù)庫服務(wù)器等應(yīng)用程序?qū)⒋蟛糠謨?nèi)容保留在進(jìn)程的地址空間中,減少將數(shù)據(jù)庫視圖的子集映射到磁盤的需求,進(jìn)而改善整體運(yùn)行性能。
32 位 Windows 支持的兩種典型的虛擬地址空間布局如下圖所示。
雖然 3GB 的虛擬地址空間好過 2GB,但依然不足以映射非常大的數(shù)據(jù)庫。為了在 32 位系統(tǒng)上解決這一問題,Windows 提供了一種名為地址窗口化擴(kuò)展(Address Windowing Extension,AWE)的機(jī)制,可以讓 32 位應(yīng)用程序最多分配 64GB 的物理內(nèi)存,隨后將視圖或窗口映射到自己的 2GB 虛擬地址空間中。雖然 AWE 將虛擬內(nèi)存到物理內(nèi)存的映射關(guān)系的管理負(fù)擔(dān)轉(zhuǎn)移到了開發(fā)者身上,但是確實(shí)滿足了直接訪問更多物理內(nèi)存的需求,具體數(shù)量甚至超過了 32 位進(jìn)程地址空間一次可以容納的上限。
64 位 Windows 為進(jìn)程提供了更大的地址空間,因?yàn)?64 位的地址長度最多可以訪問 2 的 64 次方(16 EB,1EB = 1024 PB,1PB = 1024 TB,1TB = 1024 GB)。
內(nèi)核模式和用戶模式
為了防止用戶應(yīng)用程序訪問或修改操作系統(tǒng)的重要數(shù)據(jù),Windows 使用了兩種處理器訪問模式(其實(shí)運(yùn)行 Windows 的處理器可能支持更多模式)。兩種模式分別是用戶模式和內(nèi)核模式:
- 用戶模式:應(yīng)用程序代碼運(yùn)行在用戶模式下。
- 內(nèi)核模式:操作系統(tǒng)代碼運(yùn)行在內(nèi)核模式下。內(nèi)核模式下處理器執(zhí)行模式允許訪問所有的系統(tǒng)內(nèi)存和 CPU 指令。
一些處理器會使用代碼段特權(quán)級(code privilege level)或 Ring 級這樣的術(shù)語區(qū)分不同的模式。但也有處理器使用類似監(jiān)管模式(supervisor mode)和應(yīng)用程序模式來進(jìn)行區(qū)分。
雖然每個 Windows 進(jìn)程都有自己的私有內(nèi)存空間,但是內(nèi)核模式的操作系統(tǒng)和設(shè)備驅(qū)動程序代碼共享了同一個虛擬地址空間。虛擬內(nèi)存中的每個頁都會通過標(biāo)簽指明處理器必須用哪種訪問模式讀取或?qū)懭腠?。系統(tǒng)空間中的內(nèi)存也只能從內(nèi)核模式訪問,而用戶地址空間中的所有頁都可以從用戶模式或者內(nèi)核模式訪問。
只讀頁(靜態(tài)數(shù)據(jù)的頁)在任何模式下都不可寫入。此外,對于支持不可執(zhí)行內(nèi)存保護(hù)功能的處理器,Windows 會將頁包含的數(shù)據(jù)標(biāo)記為不可執(zhí)行,這樣防止疏忽或者惡意代碼在數(shù)據(jù)區(qū)域中執(zhí)行(需要開啟數(shù)據(jù)執(zhí)行保護(hù)功能(data execution prevention,DEP))。
對于在內(nèi)核模式下運(yùn)行的組件,Windows 對它們使用的私有讀/寫系統(tǒng)內(nèi)存不提供任何保護(hù)。換句話說,一旦處于內(nèi)核模式,操作系統(tǒng)和設(shè)備驅(qū)動代碼都可以訪問整個系統(tǒng)空間內(nèi)存,并可以繞過 Windows 安全機(jī)制訪問各種對象。因?yàn)?Windows 操作系統(tǒng)有大量代碼運(yùn)行在內(nèi)核模式下,所以運(yùn)行于內(nèi)核模式的組件必須仔細(xì)設(shè)計(jì)和測試,以確保不會違反系統(tǒng)安全機(jī)制或?qū)е孪到y(tǒng)不穩(wěn)定。
這種缺乏保護(hù)的特性也使得在加載第三方設(shè)備驅(qū)動時需要更加慎重,尤其是第三方設(shè)備驅(qū)動程序不包含數(shù)字簽名,一旦進(jìn)入內(nèi)核模式,驅(qū)動程序就可以完整訪問操作系統(tǒng)的所有數(shù)據(jù)。這也是從 Windows 2000 開始進(jìn)行驅(qū)動簽名機(jī)制的原因之一。(除了簽名機(jī)制,Windows 提供了驅(qū)動程序驗(yàn)證程序進(jìn)行驅(qū)動程序測試和查找 bug,后面會說明,鏈接:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/devtest/devcon-examples#example-8-list-all-driver-files)
通過 Windows 10 自帶的性能監(jiān)控器可以觀察到當(dāng)前的內(nèi)核模式和用戶模式之間切換以及占用的耗時對比。
虛擬機(jī)監(jiān)控程序
近些年,應(yīng)用程序和軟件開發(fā)模式方面有了很大的變化,例如云服務(wù)的出現(xiàn)和無處不在的物聯(lián)網(wǎng)設(shè)備。這些新趨勢推動著操作系統(tǒng)和硬件供應(yīng)商必須設(shè)法以更高效的方式通過宿主機(jī)硬件實(shí)現(xiàn)來賓操作系統(tǒng)的虛擬化,例如可能需要通過服務(wù)器托管多個租戶,用一臺服務(wù)器運(yùn)行 100 個互相隔離的網(wǎng)站,甚至讓開發(fā)者無須購買專屬硬件即可測試幾十種不同的操作系統(tǒng)。用戶對虛擬化技術(shù)的速度、效率和安全性提出了更高的要求,進(jìn)而催生了新的計(jì)算模式和軟件理論。實(shí)際上,當(dāng)今的一些軟件,例如 Docker,本身就得到了 Windows 10 和 Windows server 2016 的支持,可以運(yùn)行在容器中,進(jìn)而獲得全面隔離的虛擬機(jī)環(huán)境,借此運(yùn)行同一個應(yīng)用程序?;蚩蚣軄韺?shí)現(xiàn)來賓/宿主機(jī)模式的革新。
為了提供此類虛擬化服務(wù),幾乎所有現(xiàn)代化的解決方案都會用到虛擬機(jī)監(jiān)控程序(hypervisor),這是一種特殊的高特權(quán)組件,可對計(jì)算機(jī)上的所有資源,從虛擬內(nèi)存和物理內(nèi)存到設(shè)備中斷,甚至到 PCI 和 USB 設(shè)備實(shí)現(xiàn)虛擬化和隔離。Hyper-V 就是這樣的一種虛擬機(jī)監(jiān)控程序,Windows 8.1 以及后續(xù)版本中的 Hyper-V 客戶端功能就是通過這項(xiàng)技術(shù)實(shí)現(xiàn)的。
在 Windows 10 中,微軟使用 Hyper-V 虛擬機(jī)監(jiān)控程序提供了一系列基于虛擬化的安全性(Virtualization Based Security,VBS)的新服務(wù):
- Device Guard(設(shè)備防護(hù))。相比僅使用 KMCS,通過虛擬機(jī)監(jiān)控程序代碼完整性(HVCI)可提供更強(qiáng)大的代碼簽名保證,并可對 Windows 操作系統(tǒng)的用戶模式和內(nèi)核模式代碼提供定制的簽名策略。
- Hyper Guard(超防護(hù))??杀Wo(hù)與內(nèi)核及虛擬機(jī)監(jiān)控程序有關(guān)的重要數(shù)據(jù)結(jié)構(gòu)和代碼。
- Credential Guard(憑據(jù)防護(hù))。可防止未經(jīng)授權(quán)訪問域賬戶的憑據(jù)和密文,并可與生物驗(yàn)證機(jī)制配合使用。
- Application Guard(應(yīng)用程序防護(hù))??蔀?Microsoft Edge 瀏覽器提供更強(qiáng)大的沙箱機(jī)制。
- Host Guardian(主機(jī)保護(hù)者)和 Shielded Fabric(受防護(hù)的構(gòu)造)??山柚摂M TPM(v-TPM)保護(hù)虛擬機(jī),防范來自基礎(chǔ)設(shè)施的威脅。
固件
Windows 組件對操作系統(tǒng)和系統(tǒng)內(nèi)核的安全程度的依賴性與日俱增,而系統(tǒng)內(nèi)核的安全性取決于虛擬機(jī)監(jiān)控程序所提供的保護(hù)。
那么這就產(chǎn)生一個問題:然后確保虛擬機(jī)監(jiān)控程序組件可以安全地加載并驗(yàn)證其內(nèi)容。通常這是啟動加載程序(boot loader)的職責(zé),但啟動加載程序本身也需要獲得同等程度的驗(yàn)證檢查,導(dǎo)致不同組件間的信任關(guān)系日趨復(fù)雜。
那么又該如何通過根信任鏈保證啟動過程是可靠且不受影響的?在現(xiàn)代化的 Windows 8 和后續(xù)系統(tǒng)中,這是通過系統(tǒng)固件實(shí)現(xiàn)的,但前提是必須使用基于 UEFI 且獲得了認(rèn)證的系統(tǒng)。
作為 Windows 的一項(xiàng)規(guī)定,同時也是 UEFI 標(biāo)準(zhǔn)的一部分,必須通過安全啟動對于啟動有關(guān)的軟件的簽名質(zhì)量提供強(qiáng)保證和要求。通過這樣的驗(yàn)證過程,即可確保從啟動過程的一開始,Windows 組件就能以安全地方式加載。此外注入受信任平臺模塊(Trtusted Platform Module,TPM)等技術(shù)也可以度量整個啟動過程,并提供相應(yīng)的證明(本地或遠(yuǎn)程證明)。
終端服務(wù)和多會話
終端服務(wù)指的是 Windows 通過一個系統(tǒng)為多個交互式用戶會話提供支持的能力。遠(yuǎn)程用戶可以借助 Windows 終端服務(wù)在其他計(jì)算機(jī)上建立會話,登錄服務(wù)器并運(yùn)行應(yīng)用程序。隨后服務(wù)器會將圖形用戶界面(Graphical User Interface,GUI)傳輸給客戶端,客戶端會將用戶輸入回傳給服務(wù)器。(類似于 X Window 系統(tǒng),Windows 允許咋服務(wù)器系統(tǒng)上云霄特定應(yīng)用程序,并將顯示畫面回傳給遠(yuǎn)程客戶端,但不需要將整個桌面?zhèn)鬏斀o遠(yuǎn)端)
第一個會話通常是服務(wù)會話,即會話 0,其中包含系統(tǒng)服務(wù)承載進(jìn)程。在計(jì)算機(jī)上通過控制臺物理登錄建立的第一個會話是會話 1,隨后通過遠(yuǎn)程桌面連接程序(Mstsc.exe)或快速用戶切換功能連接更多的會話。
Windows 客戶端版本只允許一個遠(yuǎn)程用戶連接到計(jì)算機(jī),如果連接時已有人登錄控制臺,則工作站被鎖定。也就是說,遠(yuǎn)程連接使用計(jì)算機(jī)不可以支持多人同時使用。
Windows 服務(wù)器系統(tǒng)支持兩個并發(fā)遠(yuǎn)程連接。這是為了方便進(jìn)行遠(yuǎn)程管理,例如所用的管理工具肯呢個要求用戶登錄到被管理的計(jì)算機(jī)。如果具備必要的許可并且配置為終端服務(wù)器,則可支持更多遠(yuǎn)程會話。
對象和句柄
在 Windows 操作系統(tǒng)中,內(nèi)核對象是指某個靜態(tài)定義的對象類型的單個運(yùn)行時實(shí)例。對象類型由一個系統(tǒng)定義的數(shù)據(jù)類型、針對該數(shù)據(jù)類型執(zhí)行操作的函數(shù),以及一組對象屬性構(gòu)成。
如果需要開發(fā) Windows 應(yīng)用程序,可能會遇到很多概念,例如進(jìn)程、線程、文件、事件對象等。這些對象都是基于 Windows 創(chuàng)建和管理的底層對象。在 Windows 中,進(jìn)程實(shí)際上是進(jìn)程對象類型的實(shí)例,文件是文件對象類型的實(shí)例。
對象屬性是對象中的數(shù)據(jù)字段,這些字段定義了對象的部分狀態(tài)。例如,進(jìn)程類型的對象會通過屬性包含進(jìn)程 ID、基本調(diào)度優(yōu)先級、訪問令牌對象的指針等。對象方法是指操作系統(tǒng)對象的手段,通??捎糜谧x取或更改對象屬性。例如,某個進(jìn)程的 Open 方法可接受進(jìn)程標(biāo)識符作為輸入,并將返回到對象的指針作為輸出。
對象和普通數(shù)據(jù)之間最本質(zhì)的區(qū)別是對象的內(nèi)部結(jié)構(gòu)是不透明的。必須調(diào)用對象服務(wù)才能獲得對象中存儲的數(shù)據(jù),或?qū)⑼獠繑?shù)據(jù)放入對象,而不能直接讀取或更改對象內(nèi)部的數(shù)據(jù)。這個差異將對象的底層實(shí)現(xiàn)與單純只能使用這些對象的代碼有效地區(qū)分開了,借此可以隨時輕松訪問和更改對象的具體實(shí)現(xiàn)。
借助對象管理器這個內(nèi)核組件,對象擁有可以方便完成下列四大重要操作系統(tǒng)任務(wù)的能力:
- 為系統(tǒng)資源提供易于理解的名稱
- 跨越進(jìn)程共享資源和數(shù)據(jù)
- 保護(hù)資源免遭未經(jīng)授權(quán)的訪問
- 引用跟蹤,借此系統(tǒng)可以識別某個對象什么時候不再使用,以便自動釋放。
Windows 操作系統(tǒng)中并不是所有數(shù)據(jù)結(jié)構(gòu)都是對象,只有需要共享、保護(hù)、命名或者對用戶模式程序可見的數(shù)據(jù)才有必要放在對象中。
安全性
Windows 從設(shè)計(jì)之初就充分考慮了安全性,可滿足政府與業(yè)界各類正式的安全評級需求,例如 common criteria for information technology security evaluate(CCITSE)規(guī)范。達(dá)到政府認(rèn)可的安全評級,可以讓操作系統(tǒng)在相關(guān)領(lǐng)域內(nèi)更有競爭力。當(dāng)然,這其中的很多功能能為任何多用戶系統(tǒng)帶來好處:
Windows 的核心安全功能包括以下幾個方面:
- 為文件、目錄、進(jìn)程、線程等所有可共享的系統(tǒng)對象提供酌情決定,并且強(qiáng)調(diào)應(yīng)用的保護(hù)。
- 針對主體或用戶,以及他們發(fā)起的操作執(zhí)行安全審核與問責(zé)。
- 登錄時的用戶身份驗(yàn)證。
- 防止用戶未經(jīng)授權(quán)訪問其他用戶已經(jīng)撤銷分配的資源,例如空閑內(nèi)存或磁盤。
Windows 針對對象提供了三種形式的訪問控制:
- 酌情決定的訪問控制。大多數(shù)人在想到操作系統(tǒng)安全性時,首先會想到這種保護(hù)機(jī)制。通過這種方法,對象(例如文件或打印機(jī))的所有者可以運(yùn)行或拒絕他人訪問。用戶登錄時可以獲得一系列的安全憑據(jù),也叫做安全上下文。當(dāng)用戶試圖訪問某個對象時,系統(tǒng)會將他們的安全上下文與所要訪問控制列表進(jìn)行對比,進(jìn)而判讀該用戶是否有權(quán)執(zhí)行所請求的操作。在 Windows server 2012 和 Windows 8 中,這種酌情決定的控制機(jī)制還通過基于屬性的訪問控制(動態(tài)訪問控制)進(jìn)一步加強(qiáng)。不過資源的訪問控制列表并不一定要識別個別用戶和組,還可以識別允許訪問資源所需具備的屬性或聲明,例如“許可級別:頂級機(jī)密”或“資歷:10 年”。通過借助 Active Directory 解析 SQL 數(shù)據(jù)庫和架構(gòu)自動獲得這樣的屬性,這種更優(yōu)雅,靈活的安全模型可以幫助組織擺脫手動管理組以及組層次結(jié)構(gòu)的繁瑣工作。
- 特權(quán)訪問控制。在酌情決定的訪問控制無法完全滿足需求時,這也是種必要機(jī)制。這種方法可以確保在所有者不可用時,他人依然可以訪問受保護(hù)的對象。例如,某位員工離職,管理員需要通過某種方式訪問以前只能被該員工訪問的文件,此時管理員可以在 Windows 中獲取文件的所有權(quán),隨后即可按需管理文件的訪問權(quán)。
- 強(qiáng)制完整性機(jī)制。如果需要為同一個用戶賬戶訪問的受保護(hù)對象提供額外的安全控制,此時就需要使用這種機(jī)制。很多幾首都用到了這一機(jī)制,例如為 Windows 應(yīng)用提供的沙箱機(jī)制,通過用戶配置為受保護(hù)模式的 Internet explorer 提供隔離,以及保護(hù)提權(quán)后的管理員賬戶創(chuàng)建的對象不被未經(jīng)提權(quán)的管理員賬戶訪問等。
從 Windows 8 開始,系統(tǒng)會使用一個名為 appcontainer 的沙箱承載 Windows 應(yīng)用,這種技術(shù)可以在不同的 appcontainer 之間,以及 appcontainer 與非 Windows 應(yīng)用進(jìn)程之間實(shí)現(xiàn)隔離。appcontainer 中的代碼可以通過 broker 通信,有時候還可以與其他 appcontainer 或進(jìn)程通過 Windows 運(yùn)行時所提供的完善定義的協(xié)定通信。
Windows API 接口全面融入了各種安全機(jī)制。Windows 子系統(tǒng)通過與操作系統(tǒng)類似的做法實(shí)現(xiàn)了基于對象的安全模型:
- 為共享的 Windows 對象設(shè)置 Windows 安全描述符,防止未經(jīng)授權(quán)訪問。
- 當(dāng)應(yīng)用程序首次試圖訪問一個共享對象時,Windows 子系統(tǒng)會驗(yàn)證應(yīng)用程序的權(quán)限。
- 如果安全檢查通過,Windows 子系統(tǒng)將允許應(yīng)用程序繼續(xù)訪問。
注冊表
只要用過 Windows 操作系統(tǒng),那么肯定聽說過甚至使用過注冊表。談到 Windows 內(nèi)部原理免不了提到注冊表,因?yàn)樽员磉@個系統(tǒng)數(shù)據(jù)庫中包含了
- 自動和配置系統(tǒng)必須的信息
- 控制 Windows 運(yùn)行的系統(tǒng)級軟件設(shè)置
- 安全數(shù)據(jù)庫
- 使用的屏幕保護(hù)程序等用戶配置信息
注冊表還為內(nèi)存中的易失數(shù)據(jù)提供了訪問接口,例如系統(tǒng)當(dāng)前的硬件狀態(tài)(加載了哪些設(shè)備驅(qū)動程序、驅(qū)動程序使用了哪些資源)。此外還有 Windows 性能計(jì)數(shù)器。性能計(jì)數(shù)器實(shí)際上并不真正位于注冊表中,但可以通過注冊表訪問性能計(jì)數(shù)器的詳細(xì)信息。
雖然很多 Windows 用戶和管理員永遠(yuǎn)不需要直接面對注冊表(因?yàn)榇蟛糠峙渲眠x項(xiàng)都是通過標(biāo)準(zhǔn)的管理工具查看和更改),但注冊表依然是一個很實(shí)用的 Windows 內(nèi)部信息來源,其中包含了很多可以影響系統(tǒng)性能和行為的設(shè)置。(注冊表的系統(tǒng)級配置根鍵 HKEY_LOCAL_MACHINE,簡稱 HKLM)
Unicode
Windows 與大部分其他操作系統(tǒng)有一個巨大的不同:Windows 中大部分文本字符串都是以 16 位寬的 Unicode 字符串(從技術(shù)上來看其實(shí)使用了 UTF-16LE)存儲和處理。Unicode 是一種國際化的字符集標(biāo)準(zhǔn),為全世界大部分已知字符集定義了唯一值,可為每種字符提供 8 位、16 位,甚至 32 位的編碼。
由于很多應(yīng)用程序處理的是 8 位(單字節(jié))的 ANSI 字符串,因此很多 Windows 函數(shù)可以通過兩個入口點(diǎn)接受字符串參數(shù):
- 一個 Unicode(16 位寬字符)版本
- 一個 ANSI(8 位窄字符)版本
如果調(diào)用窄字符版本的 Windows 函數(shù),則可能會對性能有微弱影響,因?yàn)檩斎氲淖址畢?shù)需要先轉(zhuǎn)換為 Unicode 字符才能被系統(tǒng)處理,并將輸出參數(shù)從 Unicode 轉(zhuǎn)換為 ANSI 輸出給應(yīng)用程序。因此,如果需要在 Windows 上運(yùn)行老版本的服務(wù)或代碼且相關(guān)代碼都是使用 ANSI 字符串編寫的,Windows 會將 ANSI 字符轉(zhuǎn)換為 Unicode 供自己使用,然而 Windows 絕對不會轉(zhuǎn)換文件內(nèi)的數(shù)據(jù),需要應(yīng)用程序決定數(shù)據(jù)需要存儲為 Unicode 還是 ANSI 的形式。
通過 Dependency Walker 工具打開 Kernel32.DLL 可以看到其中包含的函數(shù)接口,同時包含了 CreateFileA 和 CreateFileW 兩種函數(shù)接口,這就是為不同的字符串類型提供的成對的函數(shù)接口。
(Dependency Walker 工具下載鏈接:http://dependencywalker.com/)文章來源:http://www.zghlxwxcb.cn/news/detail-528886.html
總結(jié)
本文在這里僅僅將 Windows 操作系統(tǒng)中的主要的概念和術(shù)語進(jìn)行了整理和記錄,方便后面對于 Windows 內(nèi)核及驅(qū)動的學(xué)習(xí)。通過本文的閱讀能夠?qū)?Windows 操作系統(tǒng)的大致結(jié)構(gòu)和部分專業(yè)名詞有一定了了解和熟悉,具體的細(xì)節(jié)還需要深入學(xué)習(xí)和了解。文章來源地址http://www.zghlxwxcb.cn/news/detail-528886.html
到了這里,關(guān)于Windows 基本概念和術(shù)語的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!