linux_shell筆記(七)

<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

>rm Untitled/ 1.odt

上面命令是對 'Untitled 1.odt' 執行刪除。需要對空格進行轉義。

make 工具可以用來跟蹤那些更新過的模塊,並確保在編譯時使用所有程序模塊的最新版本。

CVS ,併發版本控制系統是一個源代碼管理系統,用來跟蹤項目所涉及到的文件版本。

>apropos name


>gcc –version

>pwd

>cd ~

>mkdir example

>cd example

>vim example.c &

>cat -n example.c example.h

>cat >Makefile RETURN

example:example.c example.h RETURN

gcc -o example example.c RETURN

CONTROL+D

>make

>chmod u+x example

>./example

>touch example.c

>make


預處理指令:在編譯初始階段,C 預處理器擴展預處理指令,使程序爲編譯過程的後續階段做好準備。預處理指令以'#' 開頭。

利用'<>' 將頭文件括起來,將指示預處理器在標準目錄列表查找頭文件。

可以在'-I'(i) 選項告訴預處理器在指定位置查找頭文件。

>mkdir inc include output

>mv example.c inc

>mv example.h include

>rm example

>ls *

>cat >Makefile RETURN

VPATH=.:./inc:./include RETURN

example:example.c example.h RETURN

gcc -I ./include $< -o example RETURN

mv example ./output RETURN

CONTROL+D

>make

>./example


儘管大多數C 函數可以使用任何名稱,但是每個程序必須有且只能有一個名爲main 的函數。

編譯過程分爲4 部分,C 預處理器擴展宏定義幷包含頭文件。在編譯階段根據源文件中的指令創建彙編語言代碼。然後彙編器創建計算機可讀的目標文件。在編譯的最後一個階段,連接器搜索指定的函數庫,找到程序使用的函數,並將這些函數的目標模塊與本程序的目標代碼合併在一起。默認情況下C 編譯器會鏈接標準C 函數庫libc.o ,如果希望鏈接到其他函數庫,用戶必須在命令行上使用'-l' 選項指定這些函數庫。

>gcc calc.c -lm

-l 選項使用的函數庫名稱約定,將跟在-l(L) 後面的那個字母后面追加lib 後,並添加擴展名.so 或者.a 。本例中的代表libm.so 。使用命名約定gcc 知道在/usr/lib/lib 中搜索這些函數庫。還可以使用'-L' 選項讓gcc 搜索其他目錄

>gcc pgm.c -L. -L/usr/x11R6/lib -lgraphics

該示例,gcc 在搜索/usr/lib/lib 目錄之前,在工作目錄和/usr/X11R6/lib 中搜索函數庫文件libgraphics.a

在編譯的最後一步,如果沒有使用'-o' 選項指定不同的文件名,連接器將會創建一個名爲a.out 的可執行文件。


ELF 格式 Linux 的二進制文件使用 ELF(Executable and Linking Format, 可執行可鏈接格式 ) 格式。

>pwd

>cd ~/example/src

>ls

>file example


選項'-O3' 告訴gcc 使用C 編譯器優化器。優化器使得目標代碼更加高效,這樣可執行程序可能更快的運行。

>gcc -O3 -o example example.c

可以使用gcc 命令的'-c' 選項來抑制編譯過程的連接階段。'-c' 選項不會認爲那些沒有解析的外部引用是錯誤的,用戶可以使用這一功能在創建程序模塊時進行編譯和語法調試。

>gcc -c -I ../include example.c

>ls

>gcc -o example example.o


使用共享庫:

大多數現代操作系統都使用共享庫,也稱爲動態庫。在編譯時,這些庫並不會鏈接到程序中去而是在程序開始時( 或者在某些情況下是在程序開始後) 加載進來。共享庫擴展名是.so(share object)

歸檔庫 與共享庫不同的是較舊的靜態鏈接庫,也稱爲歸檔庫。

使用共享庫的主要原因是在於減少內存用量並可增加程序的可維護性。現在他們基本已經取代了靜態庫,成爲庫類型的首選。

ldd ldd(list dynamic dependencies, 列出動態依賴關係 ) 工具告訴用戶某個程序需要那些共享庫。

>ldd example

執行動態運行時連接的程序 ld-linux.so 總是在 /usr/lib 目錄下查找函數庫。 ld 要搜索的其他目錄根據ld 安裝的方式而有所不同。在編譯時( 實際上是連接) ,還可以通過在 -r 選項後面帶上一個冒號隔開的目錄列表指定搜索路徑,向 ld 添加要查找的目錄。搜索路徑只使用絕對路徑名。 '-r' 選項後面沒有空格。

gcc -o gnome-session -r/lib:/usr/X11R6/lib

這個命令行允許ld.so 在標準目錄/usr/lib 之外的/lib/usr/X11R6/lib 目錄中搜索可執行文件所需要的庫。

編譯器需要在連接時找到共享庫,從而確保頭文件 (.h) 聲明的所需函數和過程確實存在。使用 '-L' 選項告訴編譯時連接器在目錄 mylib 中搜索共享或者靜態庫-L mylib 。與'-r' 搜索路徑不同,-L 選項可以使用相對路徑。


命令行搜索路徑是一個非常新的方法。傳統上使用LD_LIBRARY_PATH( 進來使用LD_RUN_PATH) 環境變量創建搜索路徑。這些變量格式與PATH 相同。

通常,要在常見的庫位置之前搜索LD_LIBRARY_PATH 中的目錄。使用LD_LIBRARY_PATH 帶來一些問題,因爲只能有一個環境變量,所以所有程序必須共享他,如果兩個程序使用相同的庫名稱,或者使用了同一個庫的不同且不兼容的版本,那麼只會搜索到第一個,這樣,其中一個程序就不能運行。


包裝器 LD_LIBRARY_PATH 還是可以用於稱爲包裝器的腳本中,這些腳本用於修復受損的二進制文件。假如受損的二進制文件bb 使用共享庫libbb.sobb 程序員要求用戶將這個共享庫放到/opt/bb/lib 中而不是/usr/lib 中。命令ldd bb 將告訴用戶缺少那些庫。使用下面的方法可解決這個問題:bb 重命名爲bb.broken ,並創建名爲bb/bin/sh 包裝器。

#! /bin/sh

LD_LIBRARY_PATH=/opt/bb/lib

export LD_LIBRARY_PATH

exec bb.broken “$@”


創建共享庫: 建立動態可加載共享庫並不是一項簡單的事情:它涉及可重入函數 的調用,庫入口例程的定義以及其他任務的執行。當用戶希望創建共享目標庫時,必須使用-fPIC(position-independent code, 位置無關代碼) 選項最小程度地編譯源文件,並使用連接器的-shared -x 選項將最終的目標文件連接到libxx.so

>ld -shared -x -o libmylib.so *.o


make 保持一組程序最新:當處理大型程序時,判斷由於模塊間依賴關係而要重新編譯哪些模塊是一件困難 並乏味的工作。make 工具可將這個工作自動化。


依賴行:目標文件和前提文件 在最簡單的情況下,make 在工作目錄下名爲makefile 或者Makefile 的文件中查找依賴行。依賴行指出文件之間的關係,指定某個目標文件依賴於一個或者多個前提文件。如果用戶在目標文件之前修改了它的任何一個前提文件,make 將根據依賴行後面的構造命令更新目標文件。

簡單的makefile 語法如下:

target:prerequisite-list

TAB constructing-commands

依賴行由目標和前提文件列表prerequisite-list 組成,兩者由冒號隔開。每行constructing-commands( 可能是多行) 必須以TAB 開頭,並且必須位於依賴行的下面。

依賴行上的前提條件中的每個文件也可以是另一個依賴行的目標。用戶可以將任何shell 命令放置在構造行上。makefile 的第一個目標是默認目標,當用戶不帶任何參數調用make 時,就會構建這個目標。

form : size.o length.o

gcc -o form size.o length.o

size.o:size.c form.h

gcc -c size.c

length.o:length.c form.h

gcc -c length.c

form.h:num.h table.h

cat num.h table.h > form.h

如果希望使用make 重新構建makefile 中除第一個目標之外的其他目標,就必須將這個目標作爲參數傳遞給make


隱含的依賴關係 可以依靠隱含的依賴和構造命令來輔助完成編寫makefile 的任務。

# Makefile for compute

#

compute:compute.o calc.o

gcc -o compute compute.o calc.o

compute.o:compute.c compute.h

gcc -c -O3 compute.c

calc.o:calc.c

gcc -c calc.c

clean:

rm *.o *core* *~

3 組依賴行不是必須的,因爲沒有這一行make 也能推斷出calc.o 依賴於calc.c

最後一個clean 目標沒有前提文件。

在運行make 後,如果不修改任何前提文件而再次運行makemake 將說明該程序已經是最新的,而不執行任何命令:

touch 使用touch 工具來改變前提文件的修改時間。make 工具只執行那些更新過期目標所需要的命令。

'-n' 如果用戶希望查看運行make 將要執行什麼,可使用-n 選項。'-n' 選項說明make 將要執行的命名但是並不是真正執行這些命令。

'-t' 用戶可以使用 touch 來更新所有源文件的修改時間 ,這樣make 就會認爲沒有文件是最新的,然後就重新編譯所有文件。或者可以使用 touch 或者 make '-t' 選項來更新所有目標文件的修改時間。

>touch example.c

>make -n

>make -t

>make -n


調試C 程序 沒有新聞是最好的新聞 用戶知道什麼是最好的;調試C 程序的一種方法是在源碼中的關鍵地方插入打印語句。

gcc 編譯器警告選項:所有可使編譯器行爲更加嚴格的選項都以大寫字母W 開頭。

>gcc -c - -Wall example.c

-Wimplicit 沒有顯式地聲明函數或者參數

-Wreturn-type 返回值不是空的函數。沒有返回值或者函數類型默認爲int

-Wunused 變量聲明之後未被使用

-Wcomment 字符串/* 出現在註釋中

-Wformat 某些輸入/ 輸出語句所包含的格式說明與參數不匹配

-Wall 選項顯示上面列出的所有錯誤的警告信息。

如果函數沒有明確定義類型,編譯器默認類型爲int 。如果一個程序成功執行,按照約定它應該返回0 。如果沒有返回值,那麼說明未定義退出代碼。


有很多調試器可以用來實現簡單調試方法所具備的功能。這些調試器包括gdb kgdb xxgdb ddd 等。

使用調試器可以監視和控制程序的執行。可以逐行地執行程序,此時可以檢查執行環境的狀態。

核心轉儲 調試器還允許檢查覈心文件。在程序執行過程中出現嚴重錯誤時,操作系統將創建核心文件,該文件包含着發生錯誤時該程序和系統的狀態信息。這個文件由程序正在使用的計算機內存的轉儲構成。可以使用ulimit 內置命令使得核心文件可以被保存下來。

>ulimit -c unlimited


gdb 符號調試器 爲了充分利用符號調試器來調試程序,必須使用 -g 選項來編譯程序,這個選項使得 gcc 產生調試器所需要的額外信息。這些信息包括一個符號表,即程序中用到的所有變量的名稱和他們相關值的列表。

使用-g 選項總是有幫助的,即使在用戶發佈軟件時也有用。包括調試符號會使二進制文件稍微變大。調試符號不會使程序的運行速度變慢。

當用戶編譯程序用於調試時,將優化標誌限制到 -O 或者 -O2 因爲調試和優化有着本質的不同目標,所以最好避免將這兩個操作合併。

>gcc -g example.c -o example

將優化選項完全關閉有時候可以消除錯誤。但是使用這種方式消除錯誤不應該被視爲長久的解決辦法。如果使用 -O 選項或者是 -O2 選項,正確的代碼編譯後應該可以正確的運行。 -O3 選項通常包含帶有實驗性質的優化,所以並不是所有情形下都能產生正確的代碼。


線程 線程是進程內部的單一順序控制流。線程是多線程的基礎。在多線程程序中,一個程序可以併發控制多個運行線程,每個線程執行不同的任務。


系統調用 linux 內核的 3 個基本職能是進程控制,文件系統管理和外圍設備操作。可以通過系統調用和庫函數訪問這些內核操作。系統調用指示系統根據用戶的指令執行某些工作。庫函數是間接調用,它爲用戶發送系統調用。庫例程的優點是可以將用戶從內核操作的底層細節中解脫出來,因爲這些例程都經過仔細編寫,確保能夠高效地執行。


strace 跟蹤系統調用,strace 是一調試工具,它可以顯示某個進程或者程序所發出的系統調用的軌跡。因爲沒有必要重新編譯希望追蹤的程序,所以可以對那些沒有源代碼的二進制程序使用strace 工具。


控制進程 當用戶在shell 提示符後輸入命令時,shell 進程調用fork 系統調用創建自身的副本,然後使用exec 系統調用,將另一個不同的程序覆蓋內存中的這個副本。


訪問文件系統 當程序讀取文件或者向文件中寫入的時候會產生很多動作。文件位置,文件名到inode 的轉換,用戶的訪問權限,定位文件包含文件片的所有磁盤塊。

linux 系統中,外圍設備是通過文件系統接口訪問的,每個外圍設備被表示爲一個或者多個特殊文件,通常位於/dev 目錄下,當讀取或則寫入這些特殊文件時,內核將用戶請求傳遞給適當的內核設備驅動程序。它允許對大量不同的設備使用相同的基本工具。

擁有標準系統調用和庫例程是 linux 工具可移植的關鍵。



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