每天我們都在使用Windows系統學習、編程、聽音樂、玩游戲,Windows的操作想來是很熟練了,可是你又對Windows到底了解多少呢?本系列的目的,就是讓你對Windows系統有個更直觀、更清楚、更徹底的認識。雖然我們大多數人看不到Windows的源碼,對其內存調度算法這種最深層次的技術內幕不能明窺,但是我們可以做到比現在知道的更多,了解這些之後你會發現在Windows上面開發會輕車熟路,任何木馬病毒到了你機器上不過只會成為你的試驗品。
鑒於Windows 9X內核早已淘汰,技術過時,在此不予討論。主要是針對Windows2000(Windows 5.0)以後版本,尤以2000為主。要知道xp是Windows 5.1版本,2003也才是5.2版本。那麼,對於本系列提到Windows OS技術,統指Windows 5.X技術。
一、直觀認識Windows
分析一個軟件,最容易的入手方法就是先搞清楚它的每個文件都是干嘛的。我們先來讓大家對自己正在使用的Windows 操作系統有個直觀認識。請在你使用的Windows 2000或Windows XP上,於WINNT/system32或Windows/system32目錄(系統目錄)下,找到以下幾個文件:HAL.DLL、NTOSKRNL.EXE、NTDLL.DLL、KERNEL32.dll、GDI32.DLL、USER32.DLL。這幾個是Windows系統賴以執行的關鍵,只要你能在圖形界面下操作Windows,這幾個文件就肯定在發揮作用。值得一提的是Windows的圖形界面子系統是在內核實現的(知道為什麼Linux的圖形界面怎麼著都沒Windows好看好用了吧)。
對這幾個文件的功用給出簡介如下:
HAL.DLL:Hardware Abstraction Layer,硬件抽象層。
Windows 2000也是一個軟件系統,與我們平時從事的項目開發工作一樣,項目初期需要確定開發目標。而Windows2000開發當初制定的開發目標之一就是要能夠在多種硬件平台上移植(Windows2000支持單處理器、多處理器、Compaq SystemPro),不同的硬件平台上有不同的硬件抽象層實現。在我們通常使用的標准PC上這個文件就是HAL.DLL(在Compaq SystemPro上是HALSP.DLL,滿足不同硬件平台的硬件抽象層文件在Windows安裝盤上都有,安裝時根據計算機的平台類型只復制相應的文件)。
HAL是可加載的內核模式模塊(DLL動態鏈接庫文件),它為Windows系統所運行的硬件平台提供低級接口。它隱藏硬件相關的細節,如I/O接口、中斷控制器和多處理器通信機制等這些與特定硬件結構機密相關的內容,而向操作系統內核提供統一的硬件接口函數。
NTOSKRNL.EXE:NT OS Kernel,毋庸置疑,這就是Windows系統的內核,確切說內核是在這裡面實現的,只有1.6兆,與目前Linux2.4版本內核編譯後的大小差不多。
這個文件實際上提供兩部分的主要的功能:
一是系統內核,這在硬件抽象層之上,提供系統的基本機制(線程調度和同步、內存分配等等一切你在操作系統原理書上可以看到的最復雜的那一部分,如果你覺得操作系統課程學得太抽象又有點跟自己過不去,試著把這個NTOSKRNL.EXE反匯編了讀一讀吧,不多,也就300多萬行匯編碼,只是找不著確切的入口點)。同時內核還提供硬件支持,實際上是將驅動程序等上層程序的調用對硬件進行具體化,反過來講就是硬件抽象層再抽象一次,暴露給上層一個統一的接口,寫過Windows驅動程序或是看過DDK的同學一定對Windows WDM驅動的固定編寫模式有很深的印象。
二是執行程序(稱為Executive)。這個執行程序的東東可真是多啊,我在搗騰的時候都有點讓它搞得暈暈的,不過現在來看呢其實也就一句話:執行程序是開發者與Windows之間交互的窗口,再直觀點說執行程序對外暴露Windows開發函數,經過幾次封裝後供開發者使用。我們先不管這些函數的功能分類,按功能分類的話東西有點多有點亂,容易讓人puzzled。按開發者的角度來看,有這麼幾類:Win32 API函數,這是做應用開發用的,我們常說的MFC、ATL是對其進行的面向對象化及封裝;DDK API函數和IFS kit函數,這是供驅動開發用的,DDK還耳熟些,不過如果你浏覽過DDK並且夠細心的話就會覺得奇怪為啥OS很重要的一部分——文件系統驅動的開發沒有提供,IFS(Installable File System) Kit就是做這個的啦。
我們前面說過Windows把圖形界面處理放在了內核態來執行,因此不得不提一個比較特殊的驅動:Win32k.sys,你看到的漂亮的Windows桌面就是這個文件畫出來的。
講到這裡,前述內容是Windows系統在內核態運行的基本功能,也就是說,這是運行於CPU ring0級別的(不懂什麼是ring的話趕緊去Intel的網站download Intel CPU的系統開發參考看,甭翻你手頭的微機原理課本了,那上面沒有)。還有一大堆驅動也是運行在這個級別。在內核級別上,組件之間功能的調用等等與用戶態不同,常用的是LPC,這個我們以後慢慢再講。
繼續下面的內容之前,要先說明一點事實:Windows的功能比我們平時所見的要強大的多。通常我們講Win32,實際上這只是Windows的一個子系統而已,Windows還有另外的兩個子系統:POSIX和OS/2。這麼一說的話似乎Linux上的程序可以在Windows上運行了,其實沒那麼簡單,還需要移植庫以及重新編譯連接,最終還得依賴Win32子系統的實現來完成其功能,意義不大,基本上算是被用戶們喀嚓掉了吧,我們也是只講Win32子系統。點明一點:不同的子系統有專門的子系統支持環境,POSIX子系統是POSIX.EXE,
Win32子系統是Csrss.EXE(全稱是ClIEnt/Server Run-time SubSystem)。所以,你的機器上總有Csrss.exe這樣一個進程在運行,別無聊的以為它是木馬沒事就Kill著玩。
下面是用戶級別的內容。為了方便理解,內核級別的分層介紹我們是從下往上(由硬到軟)開始,在用戶級別,我們從上往下開始介紹,這樣更直觀些。
我們先舉一個應用程序的例子(盡管用戶級別的程序遠不止應用程序這麼多),從IE講起。如果你裝了VC,並且順帶安裝了Depends這個工具,用它打開IE(或是任何一個Windows可執行文件都可),你會發現IE主程序(IEXPLORE.EXE)調用了Kernel32.DLL,而Kernel32.DLL又調用了NTDLL.DLL,同時,IE主程序還調用了USER32.DLL,USER32.DLL又有對GDI32.DLL、KERNEL32.DLL、NTDLL.DLL的調用,期間還有循環調用。
去繁從簡,Win32應用程序要調用Win32 API函數,這些函數正是有KERNEL32.DLL提供的,而KERNEL32.DLL這個文件其實並不實現具體的功能,只是做了一個簡單的地址指針轉換,把函數入口點又跳到NTDLL.DLL裡去了,對應到了相應的本機API(NATIVE API)函數上,NTDLL.DLL也不做具體的處理,通過系統功能調用將用戶級的函數調用轉換成內核模式的真正的系統功能調用,由內核執行完畢後返回應用程序進程。也許有人要問Windows為什麼要通過Kernel32.DLL和NTDLL.DLL對執行程序Executive暴露的編程接口做兩次封裝,其實這也是Win32 API和NATIVE API的區別。Win32 API又稱為存檔的API,是供用戶使用的,必須保持一致性和兼容性,不能隨隨便便修改函數命名,新函數的增加必須保持對老函數的兼容,否則使用老版本的Win32 API開發(直觀的就是我們使用win32 SDK或MFC開發)的程序在新版本的系統上可能就要運行出錯。而NATIVE API則是在系統更新時可能需要進行修改的函數,比如函數名、函數參數的類型和個數等,都有可能隨著系統升級而變更,因此是留給MS自己人來用的,當然不能直接給用戶使用。但這不是說就我們作為開發者就不能使用NATIVE API,如果你自己定位NTDLL.DLL中的函數並進行調用,只要這個函數未被MS修改,可以肯定這種調用是成功的,你的程序運行也沒有任何問題。與KERNEL32.DLL類似的還有一個ADVAPI32.DLL,提供一些比較高級的應用編程函數。
GDI32.DLL和USER32.DLL提供存檔的Win32圖形編程接口,它們也是通過NTDLL.DLL來完成對系統繪圖功能(在win32k.sys中實現)的調用。
KERNEL32.DLL、ADVAPI32.DLL、USER32.DLL和GDI32.DLL統稱為Win32子系統DLL。Win32子系統DLL將存檔的Win32 API函數“翻譯”為相應的對本機API函數調用,NTDLL.DLL將本機API“翻譯”為NTOSKRNL.EXE和Win32k.sys的內核模式系統服務調用,來完成用戶級別功能需求的實現。
講過應用程序之後,再看看用戶級別的其它程序。環境子系統(Win32子系統、POSIX子系統、OS/2子系統),這個前面提過了;服務進程,也就是你在服務管理器裡可以看到的那一堆東東,包括Services.EXE這個比較特殊的服務程序、svchost.exe等等,這些以後慢慢談;再就是系統支持進程,如SMSS.EXE(會話管理器)、WINLOGON.EXE(登陸程序)、LSASS.EXE(本機安全權限子系統)等,這些都是專門的內容,有機會將做專題研究。在這裡提到的進程都是Windows系統正常運行必要的一些進程,也就是說它們是安全的,大家不必對其有疑心(當然了,有漏洞那是例外)。
大部分用戶級別應用程序的執行要調用KERNEL32.DLL,然後再間接調用NTDLL.DLL,系統支持進程等程序有的是直接調用NTDLL.DLL。
系統機制: Windows2000為執行體、內核、設備驅動程序等核心態部分提供了一些基礎機制。先讓我們看看都有哪些:
(1)陷阱調度:包括中斷、延遲過程調用(DPC)、異步過程調用(APC)、異常處理、系統服務調度。
(2)執行體對象管理
(3)同步機制:自旋鎖、內核調度對象以及等待是如何實現的
(4)系統線程
(5)多種系統機制如Windows2000全局標志
(6)本地過程調用
下面我們就來逐個分析:
陷阱調度:
中斷和異常的實質是使CPU不按照正常的步驟來工作,硬件和軟件都能夠察覺到它們。陷阱是當異常或者中斷發生時能夠保存當前線程狀態並轉向相應處理的一種系統機制。在Windows2000中,處理器將控制交給陷阱處理程序--一些專門來處理中斷、異常的程序。
內核通過以下方式來分辨中斷和異常:中斷是一個異步事件(可以在任何時間產生),不管處理器在執行什麼程序。中斷典型地由I/O設備、時鐘、定時器產生,必要時我們可以屏蔽中斷。而異常是一個同步事件,它是由正在執行的特定代碼產生的,重新執行相同的代碼會重復產生特定的異常。比如訪問非法內存、除數為0等。系統把系統服務也作為異常來處理。
無論是硬件或者是軟件都能產生中斷和異常,比如說,一個總線異常是由硬件引起的,而除數為0顯然是軟件中的BUG導致的;同樣,I/O設備可以產生中斷,內核本身也能產生軟中斷(APC,DPC)。
當一個硬件中斷或者異常發生時,處理器收集足夠的狀態信息以保證當異常或中斷處理完畢後可以正確返回到當前執行點。處理器通過在當前線程的內核棧區建立一個陷阱框架(用來保存現場)來實現。陷阱框架通常時線程整個上下文環境的一部分。而把軟中斷當成硬中斷的一部分來處理,或者是調用內核中相應的處理程序。
在大多數情況下,當有陷阱發生時內核負責尋找相應的處理程序並且在處理程序返回時負責恢復中斷線程的繼續執行。
中斷調度:
硬件中斷是由I/O設備產生以求獲得CPU服務的,這種中斷機制使得CPU的利用率提高很多。軟件也能夠產生中斷,比如說,內核可以發起一個中斷來進行線程調度。內核在必要的時候可以關中斷,這樣CPU就可以屏蔽掉任何中斷-----這在有些時候是必要的,比如線程對於臨界區的訪問,異常處理等。
硬件中斷處理:
在X86家族裡,外部中斷在中斷控制器裡進行排隊,控制器依次中斷CPU的運行。當CPU被中斷時,它要求中斷控制器提交中斷請求,中斷控制器將中斷請求翻譯為中斷請求號,並且把這個號碼當成索引來查詢中斷調度表(IDT),並且將控制權交給相應的中斷處理程序。
中斷調度表(IDT)在系統啟動的時候就已經初始化了,裡面包括中斷號和相應處理程序的對應。
中斷請求登等級IRQL:
中斷是有等級的,這對於軟、硬中斷都是適用。內核將中斷為0---31共32個等級,等級高的權限高。內核負責將軟中斷對應到相應的等級上,HAL負責將硬件中斷對應到相應的等級上。如下圖所示:
中斷處理是按等級來運行的,並且是搶占式的,高等級的可以阻塞低等級的運行。當運行中斷處理程序時,CPU把自己的IRQL設置為要執行的中斷的IRQL,然後運行.
那麼系統是如何把硬件中斷映射到相應的IRQL的呢?答案是HAL,在Windows2000中,總線驅動能夠得知所有連接到總線上的設備,以及每個設備能發出何種中斷,緊接著總線驅動把所得到的情況匯報給PNP(即插即用管理器),它做出抉擇。最後調用HAL函數HalpGetSystemInterruptVector來實現映射。
注:該文章是認識Windows系統內核的入們文章,轉載至此希望對想深入學習Windows的朋友有所幫助,起一個拋磚引玉的作用,有對此感興趣的朋友可以交流下。只可惜此篇文章沒有找全,很是遺憾,有朋友找到的請通知一聲。。再次感謝下該文作者。。