程序裝載及靜態動態鏈接

程序裝載

程序執行的過程是 編譯-彙編-鏈接成可執行文件–通過裝載器把可以執行文件裝載到內存中,cpu從內存中讀取裝載器裝入的指令和數據。

裝載器把指令和數據裝載到內存需要滿足兩個要求:

  • 可執行程序加載後佔用的內存空間是連續的。執行指令的時候是一條一條指令的執行
  • 不能讓程序自身決定他在內存中加載的位置,因爲如果有很多程序執行的話,那麼程序本身不知道那個內存地址是否已經被佔用了。
    爲了滿足這些條件(因爲有的程序想要實現上面的功能,那系統就假裝能讓他們實現),系統用了一個辦法,在物理地址空間中找到了一塊連續的內存,分配給程序,然後把這段物理地址和程序裏需要的地址做一個映射。 這就是虛擬地址空間物理地址空間

內存分段

找出一塊物理內存和虛擬內存進行映射的方法叫做分段。但是分段會產生內存碎片
比如1GB內存分配了512MB 128MB 256MB 128MB的內存,這時候兩個128MB的資源釋放了,但是這兩個內存不是連續的如果遇到一個需要129MB內存的程序就無法加載進來。

處理辦法用了內存交換
用了交換區來作爲中介,把256MB的程序先放入交換區,然後再加載回來,把碎片空間連接在一起。

內存分頁

內存分段雖然解決了內存碎片的問題,但是每次內存交換非常耗時。
現在計算機的內存管理裏面常用內存分頁

分頁的思想就是把原來一大段的內存,細分下去,分成固定長度的一小段一小段。之後虛擬內存到物理內存的映射也根據分頁來做了。

靜態鏈接和動態鏈接

當系統啓動時,可能有非常多的程序需要通過裝載器把數據和代碼裝載到內存中,而且又很多程序會用到相同的數據和代碼,如果每次都裝載那非常消耗內存。因此就需要用鏈接的方式來達到代碼的複用。

靜態鏈接庫

鏈接器從靜態鏈接庫LIB獲取所有被引用函數,並將庫同代碼一起放到可執行文件中,應該就是編譯的時候就把所有的信息都加入。 就是非常消耗內存的方式。

  • 需要的文件: 頭文件 .h 、靜態庫 .lib
  • 頭文件.h中有函數的聲明,使用靜態鏈接庫的項目需要引用該文件才能編譯通過
  • .lib包含了實際執行代碼、符號表等等
  • 加載lib的方法:  法1.使用編譯鏈接參數或者VS的配置屬性來設置    法2.使用pragma編譯語句,例如pragma comment(lib,“a.lib”)
  • .lib中的指令將全部被直接包含在最終生成的 EXE 文件中

動態鏈接庫

允許可執行模塊(.dll文件或.exe文件)僅包含在運行時定位DLL中函數的可執行代碼所需的信息。
動態鏈接庫實現了代碼在開發階段的複用和運行階段的複用
顯式調用動態庫步驟:

1、創建一個函數指針,其指針數據類型要與調用的 DLL 引出函數相吻合。
2、通過 Win32 API 函數LoadLibrary()顯式的調用DLL,此函數返回DLL 的實例句柄。
3、通過 Win32 API 函數GetProcAddress()獲取要調用的DLL 的函數地址,把結果賦給自定義函數的指針類型。
4、使用函數指針來調用 DLL 函數。
5、最後調用完成後,通過 Win32 API 函數FreeLibrary()釋放DLL 函數。

內存、堆棧

  • 棧:操作系統自動分配釋放,存放函數值、局部變量的值,維護函數調用上下文;編譯器在編譯的時候就會把一些數據放到棧中。

常稱爲堆棧幀或者活動記錄,一般包含:
函數返回地址和參數
臨時變量:包含函數非靜態局部變量和編譯器生成的臨時變量等
保留上下文:包括函數前後調用需要保持不變的寄存器

  • 堆:一般由程序員分配釋放,如果程序員不釋放可能程序結束會主動釋放,也有可能就是內存泄漏。

分配算法:空閒鏈表 位圖 對象池

  • 可執行文件映像:存儲着可執行文件在內存中的映像,由裝載器裝載是將可執行文件的內存讀取或映射到這裏

  • 保留區:保留區並不是一個單一的內存區域,而是對內存中受到保護而禁止訪問的內存區域的總稱,如通常 C 語言講無效指針賦值爲 0(NULL),因此 0 地址正常情況下不可能有效的訪問數據

  • 段錯誤 segment fault
    用一個指針指向一個不允許讀寫的內存地址,而程序嘗試用這個指針來讀寫就會出現這個錯誤.
    將指針初始化爲 NULL,之後沒有給它一個合理的值就開始使用指針
    沒用初始化棧中的指針,指針的值一般會是隨機數,之後就直接開始使用指針

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章