《程式設計師的自我修養》第八章讀書筆記

2021-07-11 14:01:14 字數 3417 閱讀 3499

《程式設計師的自我修養》又回來了,作為一名程式設計師,我的修養還很不夠

本章主要對linux中共享庫的組織與管理方法進行分析。

作為乙個合格的軟體產品就需要維護,既然有維護就一定有更新。共享庫也不例外,開發者也會不斷修正其中的bug或改進演算法以提高共享庫的效能。但要維護乙個共享庫的相容性非常困難,因為此處的相容性是指「abi」相容性。「abi」是乙個非常難以伺候的主,稍有不滿意就會讓共享庫罷工。

既然共享庫存在相容性問題,那麼保證依賴於這些共享庫的應用程式能夠正常執行就是乙個必須要解決的問題。在linux下針對這一問題所採取的策略是「共享庫版本」。

首先來看看共享庫的命名規則:libname.so.x.y.z

其中x是主版本號,表示庫的重大公升級,不同主版本號的庫之間不相容,對於主版本公升級的庫,原有的應用程式必須重新編譯;或者保留舊版的共享庫。

y是次版本號,表示庫的增量公升級,即增加一些新的介面符號,且保持原本的符號不變。高的次版本號的庫向後相容低的次版本號的庫。

z是發布版本號,表示庫的一些錯誤的修正、效能的改進等,並不新增任何新的介面,也不對介面進行修改。相同主版本號、次版本號的共享庫,不同的發布版本號之間完全相容。

由於主版本號決定了乙個程式能否正常執行,因此linux下採用so-name機制來記錄共享庫的依賴關係。所謂依賴關係是指共享庫的檔名去掉次版本號與發布版本號,僅保留主版本號。在linux系統中系統會為每個共享庫在它所在的目錄建立乙個跟「so-name」相同的並且指向它的軟鏈結,這個軟鏈結的目的在於總是指向主版本號相同而次版本號與發布版本號最新的共享庫。如此一來就產生了兩個方面的優勢

對於所有依賴於它的程式,僅需要記錄這一so-name即可,而不需要保留編譯時所使用的共享庫,如此一來就節省了大量的磁碟空間。

同時當共享庫公升級時,就可以直接將新版的共享庫替換掉舊版,同時修改軟鏈結so-name即可。

這裡還要留心有關於glibc的符號名問題,由於某種原因,glibc 並沒有遵守這套so-name機制,請看ldd的執行結果如下:

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6

/lib/x86_64-linux-gnu/libc-2.21.so
在我機器上的libc並沒有發布版本號。

/lib64/ld-linux-x86-64.so.2

/lib/x86_64-linux-gnu/ld-2.21.so
可以發現ld也沒有驗證按照so-name機制的命名規則。

雖然so-name機制的引入帶來了一定的好處,但這一方法無法解決次版本號交會問題,所謂次版本號交會問題是指當依賴於高次版本號的程式,執行於僅有較低次版本號共享庫的系統是,就可能產生缺少某些符號的錯誤。但動態鏈結器在進行鏈結時,只進行主版本號的判斷,即只判斷so-name,若so-name相同,則認為執行環境符合條件。

對於8.2 節的具體內容就先給大家分析到這裡,可能短期內我不太會用到這部分知識。

當前linux遵照fhs(file hierarchy standard)標準對共享庫進行存放,主要位於以下三個位置:

/lib,這個位置主要存放系統最關鍵和基礎的共享庫,比如動態鏈結器、c語言執行庫、數學庫等,這些庫主要是那些/bin和/sbin 下程式所要用到的庫,還有系統啟動時需要的庫。

/usr/lib,這個目錄下主要儲存的是一些非系統執行時所需要的關鍵性的共享庫,主要是一些開發時用到的共享庫,這些共享庫一半不會被使用者程式或shell指令碼直接用到。比如objdump這個工具就是在這個目錄下。

/usr/local/lib,這個目錄用來放置一些跟作業系統本身並不十分相關的庫,主要是一些第三方的應用程式的庫。與這些庫相對於的可執行程式就放置在/usr/local/bin下。

對於乙個在動態鏈結過程中所依賴的模組,其路徑儲存在「.dynamic」,對於這部分模組動態鏈結器的查詢規則是:如果dt_need裡面儲存的是絕對路徑,那麼動態鏈結器就按照這個路徑去查詢;如果dt_need裡面儲存的是相對路徑,那麼動態鏈結器會在/lib、/usr/lib和由/etc/ld.so.conf 配置檔案指定的目錄中查詢共享庫。為了程式的可移植性和相容性,共享庫的路徑往往是相對的。但如果遍歷這些目錄查詢共享庫的話,則查詢的效率會非常低,特別是隨著所安裝的程式越來越多,ld.so.conf的內容會越來越多。因此為解決這一問題,linux 中存在乙個 ldconfig 的程式。ldconfig 命令的用途:主要是在預設搜尋目錄(/lib和/usr/lib)以及動態庫配置檔案/etc/ld.so.conf內所列的目錄下,搜尋出可共享的動態鏈結庫(格式如前介紹,lib*.so*),進而建立出動態裝入程式(ld.so)所需的連線和快取檔案,快取檔案預設為 /etc/ld.so.cache,此檔案儲存已排好序的動態鏈結庫名字列表。

程式在鏈結的時候首先從ld.so.cache中查詢,然後再到ld.so.conf 的路徑裡邊去詳細找。

8.5 節主要分享了三個環境變數,分別是ld_library_path、ld_preload、ld_debug

改變共享庫查詢路徑最簡單的方法是使用ld_library_path 環境變數,這個環境變數中包含的路徑相當於鏈結時gcc的「-l」引數。

由ld_library_path指定的路徑;

由/etc/ld.so.cache指定的路徑;

預設共享庫目錄,先/usr/lib,再/lib

ld_preload 這一環境變數可以指定預先裝載的一些共享庫甚或是目標檔案。這一環境變數指定的檔案比ld_library_path 所指定的檔案還要優先,同時無論程式是否依賴於他們,該環境變數所指定的共享庫或目標檔案都會被裝載。

書中給出了一種應用場景就是,結合全域性符號介入機制的存在,優先裝入的共享庫或目標檔案可以覆蓋標準c庫中的某個或某幾個函式而不影響其他函式。

最後乙個是ld_debug,我認為也是最有用的乙個,這個環境變數可以開啟動態鏈結器的除錯功能。可通過不同選項列印所需的資訊。

給大家分享乙個「symblos」選項,顯示符號的解析過程。

8.6 主要對建立共享庫過程所使用的編譯選項進行分析。

其中新引入的乙個編譯選項是「-wl」,這個引數可以將指定的引數傳遞給鏈結器。書中給出的乙個例子是「-wl,-soname,-my_soname」用於指定輸出共享庫的so-name。

書中還提到了幾點注意事項:

不要把輸出共享庫中的符號和除錯資訊去掉,也不要使用gcc的「-fomit-frame-pointer」選項,以上資訊的缺失會造成除錯工作的困難。

這裡給大家分享幾篇文章,有關於動態鏈結庫的查詢順序,前文中給出的並不準確,在這裡還要給大家區分以下「-l」、「-l」與「-rpath」、「ld_library_path」之間的區別,前者是編譯時用於查詢共享庫的路徑與執行時共享庫的查詢無關,而後者才是動態鏈結器查詢共享庫的路徑。「-rpath」會寫入動態鏈結庫的「.dynamic」段,「而ld_library_path」僅用於動態鏈結庫的查詢。

使用strip可以清除共享庫或可執行檔案中的符號和除錯資訊。

第八章讀書筆記

本章主要講了蜂鳴器的實驗原理,通過完整的蜂鳴器驅動控制蜂鳴器的開啟與關閉。蜂鳴器是s3c410開發板上帶的乙個硬體裝置。可以通過向暫存器寫入特定的值來控制蜂鳴器發出尖叫聲。蜂鳴器也稱為pwm脈衝寬度調製,基本原理就是通過脈衝來控制蜂鳴器的開啟和停止。pwm驅動的實現方式不同於led驅動,pwm驅動由...

第八章 讀書筆記

第八章 讓開發板發出聲音 蜂鳴器驅動 一.linux驅動的 重用。linux驅動的 重用有很多種方法。可以採用標準的c程式的方式,將要重用的 放在其他的檔案中。也可以使用另外一種動態重用的方式,也就是喲個linux驅動可以使用另外乙個linux驅動中的資源。二.強行解除安裝linux驅動。如果編寫的...

第八章讀書筆記

第8 章 重用 1,靜態重用 將要重用的 放在其他的檔案中,如果要使用某些功能,include 相應的標頭檔案即可 2,動態重用 乙個 linux 驅動可以使用另外乙個 linux 驅動中的資源 解除安裝由於異常情況而導致的linux 驅動模組無法解除安裝的情況 1.初始化函式崩潰 將當前的linu...