總述,系統如何管理正在啟動並執行所有應用程式。
1、進程解釋:
正在啟動並執行一個程式的一個執行個體定義為進程。
進程一般包含兩部分:
*一個核心對象,作業系統用它來管理進程,同時也是作業系統儲存進程統計資訊的地方。
*一個地址空間,其中包含所有可執行檔或者dll模組的代碼和資料。同時也包含動態記憶體分配,比如線程堆棧和堆的分配。
進程要做一件事情的時候,其實不是進程在做,而是只要進程一旦建立,就會隨之立刻建立一個線程,這個線程通常就叫主線程,其他的事情,或者說真正需要做的事情都有這個線程來做,包括建立更多的線程,或者執行地址空間中的代碼。當然每個線程都有自己的專屬的cpu寄存器和堆棧,作業系統通過輪詢方式為每個線程分配時間片,達到所有線程同時執行的假象。
2、windows應用程式:
windows支援兩種類型的應用程式,GUI和CUI程式。兩者界限比較模糊。只是通過vs來建立的時候會有不同的連結器開關。同時CUI不很友好介面。連結器開關不同,連結器
就會尋找正確的入口函數,如果找不到入口函數,就提示無法解析的外部命令錯誤。當出現這種連結錯誤時,可以解決辦法:配置屬性—連結器—系統—子系統,更改連結器開
關。同時更改使用字元集。
可以在crtexe.c中找到c入口函數的原始碼。
入口函數返回後,啟動函數將調用c運行庫函數exit。
3、進程執行個體控制代碼:
載入到進程地址空間的每一個可執行檔或者dll檔案都被賦予了一個唯一的執行個體控制代碼。可執行檔的執行個體被當做WinMain函數的第一個參數hIinstanceExe傳入,這個參數實際
值是一個記憶體基地址,系統將可執行檔的映像載入到進程地址空間的某個位置。可以通過函數GetModuleHandle來檢查主調進程的地址空間得到具體的這個位置。當然GetModuleHandle函數還可以返回主調進程的可執行檔的基地址。
比如函數LoadIcon和函數GetModleFileName的第一個參數HMODULE和HINSTANCE兩種類型是完全一樣的,這裡寫的不一樣是為了相容16位的windows系統。這個參數包含了那個唯一的執行個體控制代碼。
4、進程的命令列:
系統在建立一個新的進程的時候,都會傳一個命令列給這個新的進程。
首先,在任何情況下,寫入緩衝區的是時候都不應該越界。
也可以這樣通過函數GetCommandLine來得到一個緩衝區的指標,緩衝區中包含了完整的命令列。
利用在ShellAPI.h檔案中聲明並在Shell32.dll匯出的函數CommandLineToArgvW,即可以將任何Unicode字串分解成單獨的標記,(有待於理解什麼意思)
5、進程的環境變數
每一個進程都有一個與它相關聯的環境塊,這是在進程地址空間中分配的一塊記憶體。
有兩種辦法訪問這個環境塊,第一就是通過函數GetEnvironmentString來擷取完整的環境塊。使用這個函數完成後要記得用Free*函數釋放。
第二就是CUI程式專屬,通過main入口函數的參數env來得到。
通常,子進程會繼承一組和父進程一樣的環境變數的副本。不是共用同一個。
對環境變數的操作有三個函數,GetEnvironmentVariable函數可以判斷預期的環境變數是否存在。
執行字串替換的函數ExpandEnvironmentStrings函數。
可以使用SetEnvironmentVariable函數添加、刪除和修改一個變數的值。
6、進程的錯誤模式:
預設情況下,子進程會繼承父進程的錯誤模式標誌。但是也可以通過建立子進程的時候通過參數設定不讓子進程來繼承父進程(自己)的錯誤模式。
進程可以調用函數SetErroMode來告訴系統如何處理這些個錯誤。
7、進程當前所在的磁碟機和目錄
如果不提供完整的路徑名,各種windows函數會在當前磁碟機的目前的目錄尋找檔案和目錄。
一個線程可以調用以下兩個函數來擷取和設定其所在進程的當前磁碟機和目錄
GetCurrentDirectory函數和SetCurrentDirectory函數
作業系統會追蹤記錄著當前進程的磁碟機和目錄,把這些資訊儲存在進程的環境塊的環境變數中。
如果調用一個函數傳入的路徑名不是當前進程的磁碟機,那麼系統會在進程的環境塊中尋找,是否存在這個傳入的磁碟機,若果有,系統就把變數的值當做目前的目錄使用,如
果沒有找到,系統就假定指定的磁碟機的目前的目錄是它的根目錄。
windows的檔案函數從來不會添加或者更改磁碟機代號環境變數,它只是讀取這個變數。也可以使用c運行庫函數_chdir來更改目前的目錄,從而使不同磁碟機的目前的目錄得以保留。
通常情況下,子進程會繼承父進程的環境塊,如果父進程想專門給子進程單獨的環境變數,那麼父進程就要在建立子進程之前,建立磁碟機的環境變數並寫入環境塊,父進程可以調用函數GetFullPathName來得到它的目前的目錄。
8、系統版本:
可以通過函數GetVersionEx得到系統版本號碼的相關內容,其中的參數是一個比較複雜的結構體參數;
可以通過函數VerifyVersionInfo來比較主機系統的版本和要求的版本是不是一樣。
9、CreateProcess函數
10、終止進程:
終止進程方法有4種,
第一:主線程的進入點函數返回
第二:進程中的一個線程調用ExitProcess函數,當然也有ExitThread函數來結束線程。這兩種方式都有一定的隱患,前一個函數會導致清理不能正常,第二個函數會導致記憶體
泄露。
第三:任何線程都可以調用函數TerminateProcess,這個函數可以終止另一個進程或者自己的進程。
這個函數是非同步執行的(這是什麼意思?),為了確定進程是否已經終止,需要調用WaitForSingleObject函數來捕捉。
11、當進程中的所有線程終止時:
當作業系統得知一個進程中沒有任何一個線程在執行時,就會結束這個進程的存在,進程的推出代碼被設為最後一個終止的那個線程的結束代碼。
12、當進程終止運行時:
進程終止運行時,系統會做一些清理和釋放和統計計數的工作,總之,這麼說,進程建立核心對象,核心對象來管理進程的運行機制,進程的結束是這個進程本身下面還有沒有線程執行地址空間中的代碼,而核心對象的結束是使用計數是不是0.
可以通過函數GetExitCodeProcess來獲得已經終止的一個進程的結束代碼。這個函數可以在任何時候使用來監測一個進程是否結束。
13、子進程:
設計應用程式時候遇到的工作執行機制:
第一:調用函數或者調用子程式,這樣就是所謂的單任務同步機制。
第二:可以在進程裡面新建立一個線程,讓這個線程來做一定的工作,這就是所謂的非同步機制。
第三:可以新產生一個子進程,讓這個子進程來做一定的工作,這個就是所謂的既可以是同步也可以是非同步機制,但是好處是,工作期間可以保護主進程的地址空間。
這裡就涉及到一個問題,就是所謂的不同進程之間傳遞資料的方法。windows提供了這些,動態資料交換(DDE)、OLE、管道、郵件槽等。其中還有共用資料最方便的方式之一就是使用記憶體對應檔。
這裡又涉及到幾個函數,WaiteForSingleObject函數,這個函數會一直等,直到他本身的第一個參數所標識的對象變為已觸發,進程在終止的時候就會變為已經觸發。
還有這裡,良好的編程習慣,在CreateProcess建立子進程返回後,就立刻關閉(其實不是關閉,只是使用CloseHandle函數)子進程的主線程的控制代碼。
14、獨立運行子進程:
獨立啟動並執行意思是,一旦進程建立並開始執行,父進程和子進程就不再有任何通訊,就是毫無關係。比如windows資源管理員就是這樣。
為了達到這樣的獨立運行效果,就是一旦建立成功後,立刻關閉這個進程和主線程的控制代碼。
15、使用者以標準使用者權限運行時:(不是很理解,有待補充)
16、自動提升進程的許可權:
關於自動提升使用者的許可權有三種方法可以使用:
第一:向可執行檔中嵌入資訊清單檔(RT_MANIFEST),系統會自動檢查資訊清單檔中的有效欄位。
第二:將清單儲存在可執行檔同級目錄下面,尾碼名為.manifest
第三:手動在可執行檔的屬性裡面,下面有相容性裡面可以設定。
17、手動提升進程的許可權:
可以通過函數ShellExecuteEx來設定。
18、當前許可權上下文:
在這裡使用函數GetProcessElevation能夠返回提升類型和一個指出進程是否正在以管理員身份啟動並執行bool值。
19、枚舉系統中正在啟動並執行進程:
關於枚舉正在啟動並執行進程,有幾個函數可以使用:
第一:windows NTTeam Dev,開發了EnumProcesses函數,包含在PSAPI.dll中。
第二:ToolHelp API中 Process32First和Process32Next函數。
20、Process Information樣本程式