GCC 指令詳解及動態庫、靜態庫的使用( 五 )

  • 在程序編譯的最后一個階段也就是鏈接階段 , 在 gcc 命令中雖然指定了庫路徑,但是這個路徑并沒有被記錄到可執行程序中,只是檢查了這個路徑下的庫文件是否存在 。同樣對應的動態庫文件也沒有被打包到可執行程序中 , 只是在可執行程序中記錄了庫的名字 。
  • 當可執行程序被執行起來之后:
    • 程序會先檢測所需的動態庫是否可以被加載,加載不到就會提示上邊的錯誤信息 。
    • 當動態庫中的函數在程序中被調用了,這個時候動態庫才加載到內存,如果不被調用就不加載 。
動態庫的檢測和內存加載操作都是由動態鏈接器來完成的
動態鏈接器是一個獨立于應用程序的進程,屬于操作系統 。當用戶的程序需要加載動態庫的時候動態連接器就開始工作了,很顯然動態連接器根本就不知道用戶通過 gcc 編譯程序的時候通過參數 -L 指定的路徑 。
那么動態鏈接器是如何搜索某一個動態庫的呢,在它內部有一個默認的搜索順序 , 按照優先級從高到低的順序分別是:
  1. 可執行文件內部的 DT_RPATH 段 。
  2. 系統的環境變量 LD_LIBRARY_PATH 。
  3. 系統動態庫的緩存文件 /etc/ld.so.cache 。
  4. 存儲「靜態庫 / 動態庫」的系統目錄 /lib、/usr/lib 等 。
按照以上四個順序,依次搜索,找到之后結束遍歷 。若檢索到最終還是沒找到,那么動態連接器就會提示動態庫找不到的錯誤信息 。一般情況下,我們都是通過修改系統的環境變量的方式設置動態庫的地址 。
將動態庫路徑追加到環境變量 LD_LIBRARY_PATH 中:$ LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:動態庫的絕對路徑
比如,我所需要的動態庫的絕對路徑為 /mnt/hgfs/SharedFolders/DynamicLibrary , 那么:
$ LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/mnt/hgfs/SharedFolders/DynamicLibrary
這樣的話,我在運行 main,就不會報錯了 。
但是通過這種方式設置的環境變量盡在當前的終端中有效,那么怎樣才能讓這個設置永久生效呢?
通過指令$ vim ~/.bashrc打開并修改該文件:
GCC 指令詳解及動態庫、靜態庫的使用

文章插圖
修改后,使用$ source ~/.bashrc使修改立即生效 。
經過上述操作 , 就不用每次開啟終端都需要修改環境變量了 。當然這種永久生效的方式僅適用于動態庫路徑唯一的情況 , 如果你每次使用的動態庫都在不同的位置,那么這么設置也沒啥用
2.4 動態庫與靜態庫的比較2.4.1 靜態庫的特點
  1. 靜態庫對函數庫的鏈接是在編譯期完成的 。
  2. 靜態庫在程序編譯時會鏈接到目標代碼中,因此使可執行文件變大 。
  3. 當鏈接好靜態庫后,在程序運行時就不需要靜態庫了 。
  4. 對程序的更新、部署與發布不方便,需要全量更新 。
  5. 如果某一個靜態庫更新了,所有使用它的應用程序都需要重新編譯、發布給用戶 。
2.4.2 動態庫的特點
  1. 動態庫把對一些庫函數的鏈接載入推遲到程序運行時期 。
  2. 可以實現進程之間的資源共享,因此動態庫也稱為共享庫 。
  3. 將一些程序升級變得簡單,不需要重新編譯,屬于增量更新 。
2.5 使用庫的目的在項目中使用庫一般有兩個目的:
  1. 為了使程序更加簡潔不需要在項目中維護太多的源文件 。
  2. 另一方面是為了源代碼保密,畢竟不是所有人都想把自己編寫的程序開源出來 。
當我們拿到了庫文件(動態庫、靜態庫)之后要想使用還必須有這些庫中提供的 API 函數的聲明 , 也就是頭文件 , 把這些都添加到項目中,就可以快樂的寫代碼了 。
參考資料
  • GCC | 愛編程的大丙 (subingwen.cn)
  • GCC編譯的四個階段
  • Linux 靜態庫和動態庫 | 愛編程的大丙 (subingwen.cn)
  • linux命令之ar—創建靜態庫.a文件
  • 靜態庫和動態庫 - 簡書 (jianshu.com)
  • linux中 ldd命令簡介
  • collect2: error: ld returned 1 exit status(解決方案大總結)

推薦閱讀