Windows 作業系統的使用

才智咖 人氣:2.42W

Windows 規定了這個限制條件,目的是為了確保核心物件結構保持狀態的一致。下面是關於Windows 作業系統的使用,希望大家認真閱讀!

Windows 作業系統的使用

1,程序虛擬地址空間

在windows作業系統中,每個程序都有自己的私有地址空間,因此一個程序的執行緒只能訪問屬於這個程序的記憶體空間,即程序之間是地址隔離的。在windows2000中,程序虛擬地址空間可分為如下四個部分:

1)NULL 區 (0x00000000~0x0000FFFF): 如果程序中的一個執行緒試圖操作這個分割槽中的資料,CPU就會引發非法訪問。他的作用是,呼叫malloc等記憶體分配函式時,如果無法找到足夠的記憶體空間,它將返回NULL。而不進行安全性檢查。它只是假設地址分配成功,並開始訪問記憶體地址0x00000000(NULL)。由於禁止訪問記憶體的這個分割槽,因此會發生非法訪問現象,並終止這個程序的執行。

2)使用者模式分割槽 ( 0x00010000~0xBFFEFFFF):這個分割槽中存放程序的私有地址空間。一個程序無法以任何方式訪問另外一個程序駐留在這個分割槽中的資料(相同exe,通過copy-on-write來完成地址隔離)。(在windows中,所有和動態連結庫都載入到這一區域。系統同時會把該程序可以訪問的所有記憶體對映檔案對映到這一分割槽)。

2)隔離區 (0xBFFF0000~0xBFFFFFFF):這個分割槽禁止進入。任何試圖訪問這個記憶體分割槽的操作都是違規的。微軟保留這塊分割槽的目的是為了簡化作業系統的現實。

3)核心區 (0xC0000000~0xFFFFFFFF):這個分割槽存放作業系統駐留的程式碼。執行緒排程、記憶體管理、檔案系統支援、網路支援和所有裝置驅動程式程式碼都在這個分割槽載入。這個分割槽被所有程序共享。

一、在這一節,我們詳細討論一下使用者模式分割槽,使用者模式分割槽從地地址到高地址依次為:

1)程式碼段,存放函式體的二進位制程式碼。

2)靜態資料區(分為以初始化資料段和未初始化資料段)全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域, 未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。程式結束後由系統釋放 。

3)堆,一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由OS回收 。注意它與資料結構中的堆是兩回事,分配方式倒是類似於連結串列。

......(未對映部分)(這個部分包含各種匯入的dll等)

4)棧, 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。

二、下面詳細介紹exe匯入到執行的全過程,以及地址空間的載入。

1)系統找到在呼叫CreateProcess時指定的exe檔案。

2)系統建立一個新程序的核心物件。

3)系統為這個新程序建立一個私有的地址空間。

4)系統保留一個足夠大的地址空間區域,用來存放exe檔案。這個區域的位置在exe檔案中設定。預設情況下,exe檔案的基地址是0x0400000. (1.編譯器處理每個原始碼模組,生成obj檔案。2.連結程式將所有obj模組的內容組合在一起,生成一個單獨的可執行對映檔案即exe,該對映檔案包含用於可執行模組的所有二進位制程式碼以及全域性/靜態資料變數,同時也包含一個匯入部分,列出了該可執行模組所需要的所有dll模組的名字,對於每個列出的 dll名,該匯入部分指明瞭那些函式和變數符號是被可執行的二進位制程式碼所引用的)

5)在將exe檔案對映到程序的地址空間之後,系統會訪問exe 檔案中的一個段(這個段列出了一些DLL檔案),並列出exe檔案程式碼中呼叫函式dll檔案的部分。然後,系統為每個dll檔案呼叫loadlibrary函式,如果某個dll檔案需要呼叫更多的 dll,那麼系統會再次呼叫loadlibrary函式,來載入這個dll。系統保留一個足夠大的地址空間區域,用來存放這個dll檔案。預設情況下,微軟建立dll檔案基地址0x10000000。 windows提供的所有標準系統dll都有不同的基地址,這樣,即使載入到單個地址空間,他們之間也不會重疊。(1.編譯器處理每個原始碼模組,生成一個obj模組。2.連結程式將所有obj模組的內容組合在一起,生成一個單獨的dll映像檔案,該映像檔案包含用於dll的所有二進位制程式碼以及全域性/靜態資料變數。3.如果連結程式檢查到dll的原始碼模組至少匯出了一個函式或變數,則連結程式同時生成一個單獨的lib檔案,這個lib檔案很小,只是簡單地列出了所有被匯出的函式和變數的符號名)

6)當把所有的exe檔案和dll檔案都對映到程序的地址空間之後,系統就會建立一個執行緒核心物件,並使用該執行緒以DLL_PROCESS_ATTACH為引數來呼叫每個DLL的DllMain函式,當所有對映的DLL都對此通知做出相應後,系統將驅使主執行緒開始執行exe檔案的啟動程式碼(winmainCRTStartup 函式),這個函式負責對c/c++執行時庫進行初始化和呼叫函式入口函式(main 或 winmain)。

下面強調一些dll和lib的載入區別:

dll允許可執行模組(檔案或檔案)僅包含在執行時定位DLL函式的可執行程式碼所需的資訊(即將dll附帶的lib載入到可執行模組中)。

對於lib檔案,連結器從靜態連結庫LIB獲取所有被引用函式,並將庫同程式碼一起放到可執行檔案中。

三、堆和棧的理論知識

3.1申請方式

stack: 由系統自動分配。 例如,宣告在函式中一個區域性變數 int b; 系統自動在棧中為b開闢空間

heap: 需要程式設計師自己申請,並指明大小,在c中malloc函式 ,在C++中用new運算子 。

3.2 申請後系統的響應

棧:只要棧的剩餘空間大於所申請空間,系統將為程式提供記憶體,否則將報異常提示棧溢位。

堆:首先應該知道作業系統有一個記錄空閒記憶體地址的連結串列,當系統收到程式的申請時,

會遍歷該連結串列,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點連結串列中刪除,並將該結點的'空間分配給程式,另外,對於大多數系統,會在這塊記憶體空間中的首地址處記錄本次分配的大小,這樣,程式碼中的delete語句才能正確的釋放本記憶體空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閒連結串列中。

3.3申請大小的限制

棧:在Windows下,棧是向低地址擴充套件的資料結構,是一塊連續的記憶體的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩餘空間時,將提示overflow。因此,能從棧獲得的空間較小。

堆:堆是向高地址擴充套件的資料結構,是不連續的記憶體區域。這是由於系統是用連結串列來儲存的空閒記憶體地址的,自然是不連續的,而連結串列的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬記憶體。由此可見,堆獲得的空間比較靈活,也比較大。

3.4申請效率的比較:

棧由系統自動分配,速度較快。但程式設計師是無法控制的。

堆是由new分配的記憶體,一般速度比較慢,而且容易產生記憶體碎片,不過用起來最方便.

另外,在WINDOWS下,最好的方式是用VirtualAlloc分配記憶體,他不是在堆,也不是在棧是直接在程序的地址空間中保留一快記憶體,雖然用起來最不方便。但是速度快,也最靈活。

3.5堆和棧中的儲存內容

棧: 在函式呼叫時,第一個進棧的是主函式中後的下一條指令(函式呼叫語句的下一條可執行語句)的地址,然後是函式的各個引數,在大多數的C編譯器中,引數是由右往左入棧的,然後是函式中的區域性變數。注意靜態變數是不入棧的。

當本次函式呼叫結束後,區域性變數先出棧,然後是引數,最後棧頂指標指向最開始存的地址,也就是主函式中的下一條指令,程式由該點繼續執行。

堆:一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體內容有程式設計師安排。

3.6 “棧(stack)”和“堆(heap)”是兩種不同的動態資料區,棧是一種先進後出的線性結構,棧頂地址總是小於等於棧的基地址。堆是一種鏈式結構。程序的每個執行緒都有私有的“棧”,所以每個執行緒雖然程式碼一樣,但本地變數的資料都是互不干擾。一個堆疊可以通過“基地址”和“棧頂”地址來描述。全域性變數和靜態變數分配在靜態資料區,本地變數分配在動態資料區,即堆疊中。程式通過堆疊的基地址和偏移量來訪問本地變數。

四、下面說明一下啊函式的呼叫堆疊變換,來更好的理解堆疊的原理。(VS2005測試)

壓棧的順序是從高地址向低地址方向。

1)引數以從右到左的次序壓入堆疊。

2)壓入EBP的值(書上分析這個位置插入一個函式返回指令地址,但分析時沒有發現因為間隔只有4個位元組)

3)壓入區域性變數

4)返回值放入EAX暫存器中。因為win32彙編一般用eax返回結果 所以如果最終結果不是在eax裡面的話 還要把它放到eax。所以返回值的釋放過程在引數之後進行。