《Windows內核原理與實現筆記》(三)Windows引導過程

內核加載

在Intel x86系統上,Windows操作系統獲得控制首先從硬盤的主引導記錄(MBR,MasterBoot Record)開始,Windows Setup程序在安裝Windows時填充MBR(其他的磁盤管理程序也可能填充MBR)。MBR包含代碼和數據,其代碼稱爲引導代碼,在系統引導時首先獲得控制;MBR中的數據是一張分區表,指定了每個分區在磁盤上的位置和大小,以及分區的類型。當MBR中的引導代碼被執行時,它檢查分區表中的每一個分區,若找到一個已被標記爲可引導的分區(稱爲引導分區),則將該分區的第一個扇區(稱爲引導扇區)讀到內存中。由於分區表包含了每一個分區的磁盤位置,所以,引導扇區的位置很容易被確定。然後MBR的代碼將控制權交給引導扇區中的代碼。

Windows Setup程序在確定了要將Windows系統安裝到哪個分區中以後,除了可能會寫入MBR以外,還會寫入該分區的引導扇區。所以,嚴格意義上講,Windows操作系統的真正入口點應該是引導扇區中的代碼。引導分區必須被格式化成Windows所支持的文件系統,典型的文件系統格式是NTFS和FAT,引導扇區中的代碼隨硬盤文件系統格式的不同而有所不同,其職責是,給Windows提供有關該硬盤上卷的結構和格式方面的信息,並且從該卷的根目錄中讀入Windows的加載程序,即ntldr文件;然後將控制權交給ntldr的入口函數。爲了能夠從根目錄中讀入加載程序,引導扇區包含了能理解文件系統結構和讀取文件的代碼,單個扇區(512B)的代碼和數據往往不足以完成其功能,爲此,Windows的做法是,讓引導扇區中的代碼讀入其他扇區的數據,然後跳轉到下一個扇區的代碼區。這樣就可以不受單個引導扇區長度的限制,這種做法相當於將第一個引導扇區當做一個加載器(loader),而真正完成引導扇區功能的扇區隨後被加載進來並執行。這一過程對於MBR是透明的,從而保持良好的兼容性。

Intel x86處理器支持實模式和保護模式,在實模式下,處理器的寄存器都是16位的,而且不支持虛擬地址轉譯,只能訪問物理內存空間中最低的1MB內存。計算機系統的BIOS工作在實模式下,並且,當ntldr獲得控制權時,處理器仍然在實模式下運行。Ntldr文件實際上是由兩部分組成的:第一部分是實模式代碼,即首先獲得控制的代碼區;第二部分是一個標準的Windows可執行二進制文件,在ntldr中這部分被稱爲os loader。

Ntldr的實模式代碼首先獲得控制,它的任務是,完成需在16位模式下執行的初始化工作,然後爲切換到保護模式做好基本的環境準備,之後將處理器切換到保護模式(32位模式)下,這樣它就可以訪問整個32位地址空間了。最後它將控制權交給os loader。因此,ntldr中的os loader是Windows真正的32位入口程序。

Os loader剛接獲控制時,處理器雖然已經工作在保護模式下,但是它的虛擬地址轉譯機制尚未開啓,所以,處理器仍然直接使用物理地址。Os loader首先做的工作是把物理內存管起來,用一個內存描述符(memory descriptor)數組把每一段內存的大小和用途記錄下來,然後構造頁目錄和頁表,使得16MB以下的內存能夠通過頁面映射(paging)機制進行訪問,再設置好頁目錄寄存器,並打開頁面映射機制。之後,os loader繼續執行其他的初始化工作,包括I/O設備的初始化等。如果它還需要調用BIOS中的服務(比如中斷13h、中斷15h等),則必須保護好保護模式下的設置,並暫時切換回到實模式,待服務完成以後再切換到保護模式,並恢復設置。

接下來,os loader從系統分區(即引導分區)的根目錄下讀入boot.ini文件。。注意,os loader包含了讀取當前文件系統的代碼,它能夠讀取NTFS文件系統的子目錄。然後清除屏幕,並檢查在系統分區的根目錄下是否有一個有效的hiberfil.sys文件,如果存在的話,這一次引導過程轉移到休眠系統的恢復過程。因此,os loader將控制權交給一段能恢復休眠系統的內核代碼。如果當前這次引導不是休眠恢復,那麼,os loader解析boot.ini文件,如果該文件中有多個引導選項,則os loader顯示一個引導選擇菜單;如果boot.ini文件中只包含一個引導選項,那麼,此菜單不顯示,而是立即應用該引導選項。Windows的引導選項可以用來指示當前這次引導的各種參數,包括內核模塊的文件名稱、HAL的文件名稱、CPU參數、各種內存參數、調試參數,等等。

然後,os loader加載內核模塊映像文件,默認爲ntoskrnl.exe,以及HAL映像文件,默認爲hal.dll。再加載註冊表的SYSTEM儲巢,即\WINDOWS\system32\config\system文件。通過檢查SYSTEM儲巢中的設置信息,它可以知道哪些設備驅動程序必須被加載進來,即被標記爲“引導-啓動”(SERVICE_BOOT_START)的設備驅動程序。然後它加載所有這些被標記爲“引導-啓動”的設備驅動程序,以及訪問系統目錄所必需的文件系統驅動程序。注意,在此之前osloader也可以訪問系統分區,但並非通過文件系統驅動程序。至此,引導系統所需的模塊(包括內核映像、HAL,以及必要的設備驅動程序)都已經被加載到內存中。而且,在此過程中os loader也已經構造了一個參數塊,記錄下了這次引導過程中加載器所獲得的各種參數信息。參數塊的類型爲LOADER_PARAMETER_BLOCK,Windows內核在初始化過程中將會用到這些參數信息。

最後,os loader將控制權交給內核模塊的入口函數,該函數將不再返回,所以,接下來的引導過程由內核模塊繼續進行,引導扇區和系統加載器(ntldr或os loader)的使命已經完成。

內核初始化

ntldr構造了一個類型爲LOADER_PARAMETER_BLOCK的參數塊,把與系統初始化相關的參數信息包裝到此結構中,然後將控制權傳遞給內核模塊ntoskrnl.exe的入口函數。

內核的初始化主要是內核各個組件的初始化,爲了解決在初始化過程中的相互依賴性問題,內核的初始化分兩個階段進行,稱爲階段0和階段1。階段0初始化的目的是,將階段1初始化所要用到的基本數據結構建立起來。在階段0初始化過程中,中斷被禁止,因此處理器可以順序地執行自己的初始化邏輯。KiSystemStartup函數首先初始化處理器的狀態,包括調整它的IDT,初始化TSS(Task StateSegment),以及構造PCR(Processor Control Region)。然後,調用HalInitializeProcessor函數,爲當前處理器初始化其HAL中的PCR和處理器間中斷向量;接着調用KiInitializeKernel函數,執行內核初始化。最後,當前線程蛻變成一個空閒線程。

KiInitializeKernel函數是實際執行內核初始化的函數。它的職責是:初始化內核數據結構,初始化空閒線程和進程對象,初始化PCR,然後調用執行體初始化函數ExpInitializeExecutive,最後返回。ExpInitializeExecutive函數調用HalInitSystem以初始化HAL,調用ExInitSystem以初始化執行體組件的各種數據結構,調用MmInitSystem以初始化內存管理器和內存池,調用ObInitSystem以初始化對象管理器,調用SeInitSystem以初始化安全子系統,調用PsInitSystem以初始化進程/線程管理器,調用PpInitSystem以初始化即插即用管理器,調用DbgkInitialize以初始化調試子系統。通常,這些執行體組件的階段0初始化相對簡單,以初始化組件的內部狀態爲主,因而經過階段0初始化以後僅可以提供最基本的服務。

階段0初始化完成以後,系統的線程調度器開始工作。特別值得一提的是,進程管理器在階段0初始化過程(PspInitPhase0)中,除了初始化其內部的狀態變量,它也爲初始進程創建一個進程對象,並將其命名爲“Idle”。另外,它還創建了“System”進程,以及一個系統線程,此係統線程的開始例程爲Phase1Initialization函數。然而,此線程並不立即被執行,因爲在階段0初始化過程中中斷是禁止的。

KiInitializeKernel函數返回以後,KiSystemStartup啓動中斷,將當前的中斷請求級別(IRQL,Interrupt Request Level)降低到DISPATCH_LEVEL,從而允許線程調度器選擇新的線程。因此,階段0初始化完成以後,階段1初始化例程Phase1Initialization得以運行。注意,如果仔細觀察KiSystemStartup彙編函數的代碼,可以發現它在跳轉到空閒循環KiIdleLoop以前,要經過一個屏障KiBarrierWait。此屏障對於系統的第一個處理器並不起作用,而僅對後續的處理器起作用。

階段1初始化是在System進程的一個系統線程中運行的。Phase1Initialization函數調用Phase1InitializationDiscard執行階段1初始化,然後調用MmZeroPageThread函數,從而此線程蛻變成內存管理器的零頁面線程(內存管理器中負責在後臺將空閒頁面清零的輔助線程)。在多處理器或多核系統上的初始化過程。如圖下圖所示,當第一個處理器(也稱爲引導處理器,或0號處理器,或P0)執行到階段1初始化時,在Phase1InitializationDiscard的一個特定點上,它調用KeStartAllProcessors函數,以啓動其他的處理器。這些處理器從KiSystemStartup函數開始執行。

KeStartAllProcessors函數設置好前文提到的位於KiSystemStartup函數結束處的屏障KiBarrierWait,然後依次調用KiInitProcessor函數來啓動每個處理器,KiInitProcessor函數爲每個處理器構造一份狀態信息(KPROCESSOR_STATE結構),然後調用HalStartNextProcessor函數啓動該處理器。處理器狀態信息是通過調用KiInitProcessorState函數來構造的,新處理器的起始指令地址(即KPROCESSOR_STATE結構的ContextFrame.Eip成員)爲KiSystemStartup例程。KeStartAllProcessors函數在啓動了其他所有處理器以後,設置好每個處理器的控制塊(PRCB)中的相關信息,並同步所有處理器的性能計數器,最後打開屏障KiBarrierWait,允許其他處理器進入空閒線程循環。這意味着,此後其他的處理器可以按照線程調度器的選擇來運行比空閒線程優先級更高的線程了。所以,屏障KiBarrierWait可以看成是引導處理器對於非引導處理器的一個約束,在放任它們參與線程調度以前執行必要的初始處理。

非引導處理器的初始化過程雖然也執行KiSystemStartup函數,但其執行邏輯相對要簡單很多。同樣地,KiSystemStartup也調用HalInitializeProcessor和KiInitializeKernel函數來執行HAL和內核部分的初始化,並且,KiInitializeKernel函數也調用ExpInitializeExecutive,但是,ExpInitializeExecutive函數僅僅簡單地調用HalInitSystem而已。

階段1初始化是在System進程的一個系統線程中進行的,它在一個恰當的點上調用KeStartAllProcessors函數以啓動其他的處理器。階段1初始化是內核的完全初始化,它佔據了系統初始化過程中相當一部分時間。段1初始化的主函數爲Phase1InitializationDiscard。

(1) 設置全局變量InitializationPhase爲1,標誌着當前這次系統引導過程進入內核的階段1初始化。(2) 調用HalInitSystem函數,執行HAL的階段1初始化。(3) 初始化圖形引導驅動程序,顯示Windows啓動屏幕,設置引導進度條範圍(0,100)。雖然在Windows Server 2003中已不再顯示進度條,但進度指示邏輯仍然保留。(4) 調用PoInitSystem,完成電源管理器的階段0初始化。(5) 調用HalQueryRealTimeClock,初始化系統時間,這一步必須在HAL的階段1初始化以後才能進行。再調用KeSetSystemTime以設置內核的系統時間。(6) 調用KeStartAllProcessors,啓動並初始化其他的處理器。啓動了這些輔助處理器以後,重新調用HalQueryRealTimeClock以初始化系統時間,並調用KeSetSystemTime設置內核的系統時間。(7) 接下來,調用ObInitSystem,完成對象管理器的階段1初始化;調用ExInitSystem,完成執行體組件的階段1初始化;調用KeInitSystem,完成微內核部分的初始化;調用KdInitSystem,完成內核調試器的階段1初始化;調用SeInitSystem,完成安全子系統的階段1初始化。(8) 調用InbvUpdateProgressBar函數,更新進度條至10%。(9) 創建符號鏈接“\SystemRoot”。(10) 調用MmInitSystem,完成內存管理器的階段1初始化。(11) 將國家語言支持(NLS)表映射到系統空間中,然後重置翻譯表。(12) 調用CcInitializeCacheManager,初始化緩存管理器。(13) 調用CmInitSystem1,初始化配置管理器,建立起基本的註冊表對象操作,把引導時獲得的數據轉變成註冊表格式。現在,HKLM\SYSTEM和HKLM\HARDWARE已可以使用,這一步必須在I/O子系統初始化之前完成。(14) 調用CcPfInitializePrefetcher,初始化內核中的預取器(prefetcher)。(15) 進度條前進到15%。(16) 調用FsRtlInitSystem,初始化文件系統支持庫(FsRtl)。(17) 調用KdDebuggerInitialize1,即kdcom.dll中的調試器初始化。(18) 調用PpInitSystem,初始化即插即用管理器。(19) 進度條前進到20%。(20) 調用LpcInitSystem,初始化LPC子系統。(21) 現在系統時間已經正常運行,調用ExInitSystemPhase2,再次初始化執行體組件。(22) 進度條更新範圍設置爲25~75%。(23) 調用IoInitSystem,初始化I/O系統。這是系統引導過程中較爲複雜的一部分,將佔據進度條50%的範圍。IoInitSystem函數所做的工作包括:I/O子系統中的狀態變量初始化、驅動程序對象類型和設備對象類型的創建、加載“引導-啓動”類型的驅動程序、加載“系統-啓動”類型的驅動程序,以及WMI初始化等。詳細的執行過程,請參考IoInitSystem函數的代碼,位於base\ntos\io\iomgr\ioinit.c文件中。(24) 進度條更新範圍恢復到0~100%。(25) 再次調用MmInitSystem,將當前已加載內核模塊中的PAGE段標記爲“可換頁”。(26) 進度條前進到80%。(27) 調用PoInitSystem,完成電源管理器的階段1初始化。(28) 調用PsInitSystem,完成進程管理器的階段1初始化。(29) 進度條前進到85%。(30) 調用SeRmInitPhase1,執行安全引用監視器(SRM)的階段1初始化,包括創建安全引用監視器的命令服務線程。該線程創建一個名爲“引用監視器命令端口”的LPC端口,以接收來自lsass.exe進程的命令。參見SeRmInitPhase1函數的代碼。(31) 進度條前進到90%。(32) 創建會話管理器子系統進程(smss.exe)。首先準備各種參數信息(RTL_USER_PROCESS_PARAMETERS結構),包括環境參數字符串,然後調用RtlCreateUserProcess函數以創建smss進程。(33) 進度條前進到100%。(34) 最後,調用ZwWaitForSingleObject函數,在smss進程的句柄上等待,超時值設置爲5s。如果等待成功,則意味着在5s內smss進程退出了,於是調用“KeBugCheck(SESSION5_INITIALIZATION_FAILED)”,系統崩潰;若等待超時,則認爲會話管理器已經正常運行,於是階段1初始化完成,當前線程蛻變成零頁面線程。

階段1初始化完成以後,內核已經完全初始化,執行體的各個組件進入正常運行狀態。但作爲一個操作系統,Windows的引導過程尚未完成,僅僅內核正常工作還不夠,系統必須讓應用程序也能夠運行起來。接下來的引導工作由剛剛啓動起來的smss進程繼續進行。

建立用戶登錄會話

Windows內核在階段1初始化的最後,啓動了一個用戶模式進程——會話管理器子系統(smss)。Smss進程是Windows操作系統的關鍵組成部分,它儘管是一個用戶模式進程,但有其特殊性:首先,它是可信的,這意味着它可以做一些其他用戶進程無法做的事情,比如創建安全令牌;其次,它直接建立在Windows內核的基礎上,只使用Windows內核提供的系統服務,而不依賴於任何一個環境子系統。這不難理解,因爲smss啓動時,Windows子系統尚未啓動,而且,啓動Windows子系統本身正是smss的任務之一。

Smss進程啓動以後,繼續完成系統引導過程。Smss做的工作有相當一部分依賴於註冊表中的設置。在內核的階段1初始化過程中,配置管理器已經初始化,但是直到smss進程啓動起來,只有SYSTEM儲巢已被加載到內存中。加載其他的儲巢也是smss的職責,它通過調用系統服務NtInitializeRegistry來初始化註冊表。在NtInitializeRegistry系統服務中,除了用戶輪廓儲巢以外的所有其他儲巢,均會被加載到系統中,並且註冊表樹也將建立起來。實際上,加載這些儲巢的工作由CmpInitializeHiveList函數來完成,它僅僅加載6個儲巢(包括SYSTEM儲巢)。

Smss在註冊表中的主鍵是HKLM\SYSTEM\CurrentControlSet\Control\Session Manager,用於指示smss在系統初始化階段做一些必要的工作。

按照Session Manager鍵中的指示,smss主線程完成以下事項:

 運行在啓動時執行的程序,這些程序由BootExecute值指定。執行啓動時的文件刪除或重命名任務,這由FileRenameOperations子鍵來指定。打開已知的DLL,並且在對象管理器名字空間的\KnownDlls目錄下創建相應的內存區對象,這些已知DLL的列表位於KnownDLLs子鍵中。已知DLL是系統全局共享DLL,包括子系統DLL等,各種應用程序通常都會用到這些DLL。· 創建頁面文件,頁面文件列表由Memory Management子鍵中的PagingFiles值指定。· 建立系統的全局環境變量,這些環境變量由Environment鍵下的值指定。· 加載Windows子系統的內核模式模塊(win32k.sys),這是通過系統服務NtSetSystemInformation來完成的,該函數也會調用win32k.sys中的初始化例程(即入口函數)。子系統內核模塊的文件路徑由SubSytstems子鍵的Kmode值指定。關於Windows子系統的初始化過程,請參考9.2節。· 啓動Windows子系統進程(csrss.exe)。子系統進程的命令行字符串由SubSystems子鍵的Windows值指定。

如果利用Microsoft提供的符號信息來調試WRK系統,則可以觀察到以上這些行爲均在smss主模塊(smss.exe)的SmpLoadDataFromRegistry函數中完成。由此也可以看出,Windows子系統作爲會話(session)的一部分,它的實例由smss來啓動。Smss除了依據註冊表中的設置來完成必要的系統引導工作以外,還執行以下的步驟,以進一步提供多會話和本地登錄服務:

創建LPC端口對象(\SmApiPort),以接收“加載子系統”或“創建會話”的請求。· 啓動登錄進程(winlogon.exe),登錄進程將會承擔起與用戶登錄有關的事項。

Smss的主線程在完成了以上描述的初始化工作以後,將在csrss進程和winlogon進程的句柄上等待。一旦等待成功,則意味着這兩個進程中至少有一個退出了,於是系統崩潰。Windows操作系統依賴於這兩個進程,所以,它們也是保持Windows操作系統正常運行不可缺少的組成部分。

winlogon進程

接下來引導過程轉到了winlogon進程。它的職責包括:

創建初始的窗口站(WinSta0),並且爲該窗口站創建一個桌面線程和RIT(Raw InputThread)以便接收標準輸入。· 創建登錄桌面和默認桌面。登錄桌面只有winlogon進程纔可以訪問,因而也稱爲winlogon桌面;而默認桌面允許其他進程訪問。因此,當登錄桌面活動時,其他進程無法訪問與該桌面關聯的代碼或數據。Windows用這種方式來保護與口令相關的操作,以及鎖定桌面或解除桌面鎖定這樣的安全操作。· 啓動服務控制管理器(SCM,Service Control Manager)進程(services.exe)。在啓動Windows服務的過程中,會有更多的窗口站被創建。SCM進程加載所有“自動-啓動”類型的服務和設備驅動程序。· 啓動本地安全權威子系統(lsass)進程。然後與它建立一個LPC連接(LsaAuthenticationPort端口),以便在登錄、註銷和口令操作過程中交換信息。

窗口站、桌面、桌面線程和RIT是Windows窗口管理中的重要組成部分,由Windows子系統內核模塊win32k.sys實現。Winlogon的登錄是通過一種稱爲GINA(圖形化識別和認證,Graphical Identification and Authentication)的可擴展機制來完成的。當winlogon通過GINA獲得了用戶名和口令以後,它首先調用lsass函數LsaLookupAuthenticationPackage以獲得一個認證包的句柄,然後調用LsaLogonUser將登錄信息傳遞給認證包。一旦認證包認證了當前用戶,則winlogon繼續該用戶的登錄過程;否則認證失敗。因此,認證過程是由lsass來完成的。在登錄過程的最後,winlogon檢查註冊表HKLM\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon\Userinit的值,並創建一個進程來運行該值字符串。該值串的默認值爲userinit.exe程序的路徑。Userinit進程加載當前登錄用戶的輪廓,然後檢查HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell的值,並創建一個進程來運行該值字符串;如果該值不存在,則運行HKLM\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon\Shell的值,其默認值爲explorer.exe。然後,userinit進程退出。由於當前登錄會話的Shell程序(explorer.exe)已經啓動,因此用戶可以在桌面上操作了。

至此,引導過程結束,用戶登錄到系統中,並可通過explorer.exe程序的用戶界面操作系統中的資源,例如文件系統中的目錄和文件;也可以啓動各種應用程序。在系統引導過程中,有多個進程被創建,包括smss、csrss、winlogon、SCM(services.exe)、lsass、userinit(登錄完成後自動退出)、explorer等。這些進程都是操作系統的一部分,而且大多數還是可信的。實際上,在系統啓動以後,當用戶開始在Shell程序中操作時,他們通常可以看到更多的進程,這其中有些進程是由SCM啓動並初始化的。在Windows中,SCM提供了在系統啓動時啓動進程的機制,這些進程被稱爲服務(service)進程,它們類似於UNIX中的守護進程。

Windows服務的設置信息中,瞭解以下幾個值將有助於理解一個服務或驅動程序的啓動或加載過程:

Type:這是一個DWORD值,代表該服務的類型。1、2、8分別代表設備驅動程序(SERVICE_KERNEL_DRIVER)、文件系統驅動程序(SERVICE_FILE_SYSTEM_ DRIVER)和文件系統識別器驅動程序(SERVICE_RECOGNIZER_DRIVER),這三種類型適用於內核模式驅動程序。16、32、256分別代表獨享進程的服務(SERVICE_WIN32_OWN_PROCESS)、可共享進程的服務(SERVICE_WIN32_SHARE_PROCESS),以及允許交互式輸入/輸出的服務(SERVICE_INTERACTIVE_PROCESS),這三種類型適用於Windows服務,顯然前兩者是不相容的。上面例子中的EventSystem服務是一個可共享進程的服務。

Start:這也是一個DWORD值,代表一個服務或設備驅動程序的啓動類型。0表示“引導-啓動”驅動程序(SERVICE_BOOT_START),由ntldr加載;1表示“系統-啓動”驅動程序(SERVICE_SYSTEM_START),在內核初始化過程中加載;2表示“自動-啓動”驅動程序或服務(SERVICE_AUTO_START),在引導過程中當SCM啓動起來以後啓動;3表示“按需-啓動”驅動程序或服務(SERVICE_DEMAND_START),由SCM根據需要啓動。ImagePath:一個字符串值,指定了該服務或驅動程序的可執行文件路徑。

ObjectName:一個字符串值,僅適用於Windows服務。此字符串值指定了該服務將在哪個賬戶下運行。

Group:一個字符串值,指定了一個組名稱,以指示該服務或驅動程序屬於一個組。SCM使用組的概念來決定啓動服務或加載驅動程序的順序。當SCM啓動服務時,它會根據註冊表鍵HKLM\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder中的List值指定的組順序來安排Windows服務的啓動順序。由於Windows服務之間可能存在依賴性,因此SCM必須謹慎地檢查每一個服務與它所依賴的服務是否存在前後順序矛盾的情形,即循環相依性。若一個服務存在循環相依性,則SCM不會啓動它。SCM將在啓動了List值中列出的所有組以後,再啓動所有屬於其他組的服務,最後,再啓動那些不屬於任何一個組的服務。

DependOnGroup、DependOnService:它們是多字符串值,指定了該服務或驅動程序依賴於其他哪些組或服務。SCM依據這兩個值來判斷一個服務是否循環依賴於其他的服務。如果按照組的順序排列下來,一個服務所依賴的組中還沒有任何一個服務被啓動,那麼,SCM不會啓動該服務。所以,在List值的組順序中,前一個組中的服務不能依賴於後面的組或者後面組中的服務。如果一個服務依賴於同一組內的其他服務,並且後者尚未啓動,則SCM會先跳過此服務,然後繼續啓動該組內的其他服務,待遍歷該組內所有其他服務以後再從頭檢查每一個服務。所以,對於同一個組內的服務,SCM利用多遍掃描的方法來檢查相依性,直至每一個服務要麼被啓動,要麼被確認存在循環相依性。

發佈了168 篇原創文章 · 獲贊 17 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章