使用 Strace 和 GDB 调试工具的乐趣
編寫 UNIX? 系統(tǒng)程序充滿樂趣,并且具有教育意義。使用 UNIX strace 工具和 GDB(GNU 項(xiàng)目調(diào)試工具),您可以真正地深入研究系統(tǒng)的功能,并了解組成這些功能的各種各樣的程序。同時(shí)使用這兩種工具,能夠在查看 UNIX 計(jì)算機(jī)底層信息?的時(shí)候,給您帶來更好的體驗(yàn)。
UNIX 家族總是為用戶提供了豐富的工具。UNIX 是一個(gè)工具財(cái)寶箱,有了這些工具,您不僅可以完成具有創(chuàng)造性的工作,還可以在深入研究該操作系統(tǒng)的同時(shí)得到教育和娛樂。strace(用來跟蹤任何程序的系統(tǒng)調(diào)用)和 GDB 調(diào)試工具(用來在受控的環(huán)境中運(yùn)行程序的功能齊全的調(diào)試工具)是實(shí)現(xiàn)這個(gè)目標(biāo)的兩個(gè)有價(jià)值的工具。
UNIX 的設(shè)計(jì)由大量的函數(shù)調(diào)用(稱為系統(tǒng)調(diào)用)組成,其中包括一些簡單的任務(wù),如在屏幕上顯示字符串來設(shè)置任務(wù)優(yōu)先級(jí)。所有的 UNIX 程序都是通過調(diào)用操作系統(tǒng)提供的這些底層服務(wù)來完成它們的任務(wù),使用 strace 工具,您可以清楚地看到這些調(diào)用過程及其使用的參數(shù)。通過這種方式,您可以操作這些程序,以了解它們與操作系統(tǒng)之間的底層交互。
開始游戲
讓我們以一個(gè)簡單的 UNIX 命令?pwd?作為開始,然后更深入地研究該命令在完成其任務(wù)的過程中進(jìn)行了哪些工作。啟動(dòng) xterm 以創(chuàng)建一個(gè)進(jìn)行實(shí)驗(yàn)的受控環(huán)境,然后輸入下面的命令:
$ pwd這個(gè)?pwd?命令顯示了當(dāng)前的工作目錄。在我的計(jì)算機(jī)上,當(dāng)時(shí)的輸出是:
/home/bill/一個(gè)如此簡單的函數(shù)掩飾了該命令底層的復(fù)雜性(順便說一下,所有的計(jì)算機(jī)程序都是這樣的)。要真正地了解其復(fù)雜性,請(qǐng)使用 strace 工具再次運(yùn)行?pwd?命令:
$ strace pwd通過該命令,您可以看到,在顯示和列舉當(dāng)前工作目錄的過程中,UNIX 計(jì)算機(jī)執(zhí)行了相當(dāng)多的操作(請(qǐng)參見清單 1)。
清單 1:strace pwd 命令的輸出
execve("/bin/pwd", ["pwd"], [/* 39 vars */]) = 0 uname({sys="Linux", node="sammy", ...}) = 0 brk(0) = 0x804c000 old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4001......fstat64(3, {st_mode=S_IFREG|0644, st_size=115031, ...}) = 0 old_mmap(NULL, 115031, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000 close(3) = 0 open("/lib/tls/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\360U\1"..., 1024) = 1024 fstat64(3, {st_mode=S_IFREG|0755, st_size=1547996, ...}) = 0 old_mmap(0x42000000, 1257224, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x42000000 mprotect(0x4212e000, 20232, PROT_NONE) = 0 old_mmap(0x4212e000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x12e000)... old_mmap(0x42131000, 7944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,... close(3) = 0 set_thread_area({entry_number:-1 -> 6, base_addr:0x40016ac0, limit:1048575, seg_32bit... munmap(0x40017000, 115031) = 0 brk(0) = 0x804c000 brk(0x804d000) = 0x804d000 brk(0) = 0x804d000 open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=30301680, ...}) = 0 mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000 close(3) = 0 brk(0) = 0x804d000 brk(0x804e000) = 0x804e000 getcwd("/home/bill", 4096) = 11 fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 6), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4021700... write(1, "/home/bill\n", 11/home/bill ) = 11 munmap(0x40217000, 4096) = 0 exit_group(0) = ?回頁首
UNIX 系統(tǒng)調(diào)用的具體細(xì)節(jié)
關(guān)于檢索和顯示當(dāng)前工作目錄所需的所有系統(tǒng)調(diào)用的詳細(xì)細(xì)節(jié),已經(jīng)超出了本文的討論范圍,但我會(huì)介紹如何獲取這些信息。清單 1?中的每一行都以類似?C?的格式,清楚地說明了一項(xiàng)系統(tǒng)調(diào)用及其參數(shù),這也是?C?程序員希望看到的。Strace 同樣以這種方式顯示這些調(diào)用,不管在實(shí)際創(chuàng)建該程序時(shí)使用的是何種編程語言。
如果要了解清單 1?中的所有細(xì)節(jié)信息,對(duì)于所有這些系統(tǒng)調(diào)用,UNIX 為您提供了大量的文檔。清單 1?中“最重要”的函數(shù)是?getcwd()?函數(shù),它表示獲取當(dāng)前工作目錄。當(dāng)前的 xterm 顯示了?strace pwd?的輸出,同時(shí)再啟動(dòng)另一個(gè) xterm 并輸入下面的命令以查看 UNIX 對(duì)該函數(shù)的顯示:
$ man getcwd您所看到的應(yīng)該是?getcwd()?函數(shù)完整的清單以及這個(gè)重要的?C?函數(shù)需要的和返回的參數(shù)清單。同樣地,您可以輸入?man brk?或?man fstat64等等。通常,UNIX 系統(tǒng)通過文檔對(duì)這些系統(tǒng)函數(shù)進(jìn)行了詳細(xì)的說明,如果花些時(shí)間仔細(xì)地研究它們,您將逐漸地了解到 UNIX 的功能是多么的強(qiáng)大,以及學(xué)習(xí)這些底層系統(tǒng)細(xì)節(jié)是多么的容易。在所有的操作系統(tǒng)中,UNIX 最善于幫助您理解其底層的處理過程。
回頁首
觀察 nweb
對(duì)于下面幾個(gè)步驟,您需要使用更龐大且更復(fù)雜的程序,而不是像?pwd?這樣簡單的 UNIX 命令。簡單的超文本傳輸協(xié)議 (HTTP) 服務(wù)器,如 nweb,是非常適合的。當(dāng)您在 Internet 上沖浪?的時(shí)候,HTTP 服務(wù)器偵聽瀏覽器請(qǐng)求,然后通過發(fā)送所請(qǐng)求的對(duì)象,如 Web 頁面和圖形文件,以此響應(yīng)瀏覽器的請(qǐng)求。
下載并安裝 nweb,該軟件由 IBM developerWorks 投稿作家 Nigel Griffiths 編寫。 (請(qǐng)參閱參考資料部分提供的 Nigel 的文章“nweb: ?"a tiny, safe Web server (static pages only)”(developerWorks,2004 年 6 月)的鏈接。)
下載 es-nweb.zip 到 $HOME/downloads 目錄,然后輸入清單 2?中所示的簡單命令,以提取、編譯并啟動(dòng)該程序:
注意:我假設(shè)您需要為 Linux? 工作站編譯這個(gè)程序。如果實(shí)際情況并非如此,那么有關(guān)在其他的 UNIX 操作系統(tǒng)上對(duì)該程序進(jìn)行編譯的詳細(xì)信息,請(qǐng)閱讀這篇 nweb 文章。
清單 2. 用于提取、編譯和啟動(dòng) nweb 的命令
$ cd src $ mkdir nweb $ cd nweb $ unzip $HOME/downloads/es-nweb.zip $ gcc -ggdb -O -DLINUX nweb.c -o nweb $ ./nweb 9090 $HOME/src/nweb &注意:清單 2?中的?-ggdb?選項(xiàng)和 Nigel 的文章中的內(nèi)容有些不同,該選項(xiàng)用于告訴 GCC 編譯器對(duì)該程序進(jìn)行優(yōu)化,以便使用 GDB 調(diào)試工具對(duì)其進(jìn)行調(diào)試,您將在以后用到該調(diào)試工具。
接下來,要確認(rèn) nweb 服務(wù)器已經(jīng)運(yùn)行,可以使用清單 3?中所示的?ps?命令來對(duì)它進(jìn)行檢查。
清單 3. ps 命令
$ psPID TTY TIME CMD2913 pts/5 00:00:00 bash4009 pts/5 00:00:00 nweb4011 pts/5 00:00:00 ps最后,要確認(rèn) nweb 確實(shí)正在運(yùn)行并且狀態(tài)正常,可以在您的計(jì)算機(jī)上啟動(dòng)一個(gè) Web 瀏覽器并在地址欄中輸入?http://localhost:9090。
針對(duì) nweb 使用 strace
現(xiàn)在,讓我們來進(jìn)行一些有趣的工作。啟動(dòng)另一個(gè) xterm,然后使用 strace 來跟蹤正在運(yùn)行的 nweb 服務(wù)器。要完成該任務(wù),您必須清楚該程序的進(jìn)程 ID,并且必須具有適當(dāng)?shù)臋?quán)限。您僅僅可以看到一組特定的系統(tǒng)調(diào)用,即那些與網(wǎng)絡(luò)相關(guān)的系統(tǒng)調(diào)用。輸入清單 4?第一行所示的命令作為開始,其中使用了前面顯示的 nweb 的進(jìn)程 ID。您應(yīng)該看到如下的輸出(清單 4?中的第二行)。
清單 4. 開始對(duì) nweb 進(jìn)行跟蹤
$ strace -e trace=network -p 4009 accept(0,請(qǐng)注意,在調(diào)用網(wǎng)絡(luò)?accept()?函數(shù)的過程中停止了跟蹤操作。在瀏覽器中刷新幾次?http://localhost:9090?頁面,請(qǐng)注意每次刷新該頁面時(shí) strace 的顯示。這是不是很棒呢?您所看到的是,當(dāng) Web 瀏覽器調(diào)用 HTTP 服務(wù)器 (nweb) 時(shí),服務(wù)器所進(jìn)行的底層網(wǎng)絡(luò)調(diào)用。簡單地說,nweb?正在接受?來自您的瀏覽器的調(diào)用。
您可以在運(yùn)行 strace 的具有窗口焦點(diǎn)的 xterm 中按下 Ctrl+C 以停止對(duì)網(wǎng)絡(luò)調(diào)用的跟蹤。
回頁首
再來研究 GDB 調(diào)試工具
正如您所看到的,strace 可以作為了解用戶程序如何通過某些系統(tǒng)調(diào)用與操作系統(tǒng)進(jìn)行交互的一個(gè)很好的程序。GDB 調(diào)試工具本身也可以附加于一個(gè)正在運(yùn)行的進(jìn)程,并幫助您進(jìn)行更深入的研究。
GDB 調(diào)試工具非常有用,Internet 上提供了大量的有關(guān)該工具的可用信息。通常,調(diào)試工具是很有價(jià)值的工具,并且任何負(fù)責(zé)開發(fā)和維護(hù)計(jì)算機(jī)系統(tǒng)的人員應(yīng)該了解如何使用它們。因此,在 nweb 運(yùn)行于另一個(gè) xterm 會(huì)話的同時(shí),按下 Ctrl+C 停止 strace,然后輸入清單 5?中所示的命令啟動(dòng) GDB 調(diào)試工具。
清單 5. 啟動(dòng) GDB 調(diào)試工具
$ gdb --quiet (gdb) attach 4009 Attaching to process 4009 Reading symbols from /home/bill/src/nweb/nweb...done. Reading symbols from /lib/tls/libc.so.6...done. Loaded symbols for /lib/tls/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 0xffffe410 in ?? () (gdb)-quiet?選項(xiàng)告訴 GDB 調(diào)試工具僅顯示其提示符,而不要顯示所有其他的啟動(dòng)信息。如果需要顯示額外的文本信息,可以去掉?-quiet?選項(xiàng)。
attach 4009?命令啟動(dòng)對(duì)當(dāng)前正在運(yùn)行的 nweb 服務(wù)器的調(diào)試工作,并且 GDB 調(diào)試工具通過讀取有關(guān)該進(jìn)程的所有的符號(hào)信息來做出同樣方式的響應(yīng)。下一步,使用?info?命令來列舉您所研究的程序的相關(guān)信息(請(qǐng)參見清單 6)。
清單 6. info 命令列出程序信息
(gdb) info proc process 4009 cmdline = './nweb' cwd = '/home/bill/src/nweb' exe = '/home/bill/src/nweb/nweb' (gdb)info?命令(請(qǐng)參見清單 7)的另一個(gè)有用的變種是?info functions,然而,函數(shù)的列表可能很長。
清單 7. info functions 命令得到的函數(shù)列表
(gdb) info functions All defined functions:File nweb.c: void log(int, char *, char *, int); int main(int, char **); void web(int, int);File __finite: int __finite();... (gdb)因?yàn)槭褂?-ggdb?選項(xiàng)對(duì) nweb 程序進(jìn)行了編譯,所以可執(zhí)行文件中包含了大量的調(diào)試信息,允許該調(diào)試工具查看文件中列舉的已定義的函數(shù),如清單 7?所示。
回頁首
list 和 disassemble 命令
有兩個(gè)重要的 GDB 調(diào)試工具命令,它們分別是?list?和?disassemble。通過使用清單 8?中所示的代碼嘗試使用這些命令。
清單 8. list 命令
(gdb) list main 121 exit(1); 122 } 123 124 125 main(int argc, char **argv) 126 { 127 int i, port, pid, listenfd, socketfd, hit; 128 size_t length; 129 char *str; 130 static struct sockaddr_in cli_addr; /* static = initialised to zeros */ (gdb) 131 static struct sockaddr_in serv_addr; /* static = initialised to zeros */ 132 133 if( argc < 3 || argc > 3 || !strcmp(argv[1], "-?") ) { 134 (void)printf("hint: nweb Port-Number Top-Directory\n\n" 135 "\tnweb is a small and very safe mini web server\n" 136 "\tnweb only servers out file/web pages with extensions named below\n" 137 "\t and only from the named directory or its sub-directories.\n" 138 "\tThere is no fancy features = safe and secure.\n\n" 139 "\tExample: nweb 8181 /home/nwebdir &\n\n" 140 "\tOnly Supports:");正如您所看到的,list?命令以源文件的形式列出了正在運(yùn)行的程序,并標(biāo)注了相應(yīng)的行號(hào)。按下 Return 鍵(如第 130 行和第 131 行之間所示)以接著上次的列表繼續(xù)進(jìn)行列舉?,F(xiàn)在,嘗試使用?disassemble?命令,可以縮寫為?disass(請(qǐng)參見清單 9)。
清單 9. disassemble 命令
(gdb) disass main Dump of assembler code for function main: 0x08048ba2 <main+0>: push ebp 0x08048ba3 <main+1>: mov ebp,esp 0x08048ba5 <main+3>: push edi 0x08048ba6 <main+4>: push esi 0x08048ba7 <main+5>: push ebx 0x08048ba8 <main+6>: sub esp,0xc 0x08048bab <main+9>: mov ebx,DWORD PTR [ebp+12] 0x08048bae <main+12>: and esp,0xfffffff0... 0x08048c01 <main+95>: call 0x8048664 <printf> 0x08048c06 <main+100>: add esp,0x10 0x08048c09 <main+103>: inc esi 0x08048c0a <main+104>: cmp DWORD PTR [ebx+esi],0x0 ---Type <return> to continue, or q <return> to quit---反匯編清單顯示了該?main?函數(shù)的匯編語言清單。在本示例中,匯編代碼指示出運(yùn)行該代碼的計(jì)算機(jī)使用的是 Intel? Pentium? 處理器。如果該程序運(yùn)行于不同類型的處理器上,如基于 IBM Power PC? 的計(jì)算機(jī),那么您的代碼看上去將有很大的區(qū)別。
回頁首
在其運(yùn)行的過程中進(jìn)行監(jiān)視
因?yàn)槟O(jiān)視的是一個(gè)正在運(yùn)行的程序,所以可以設(shè)置相應(yīng)的斷點(diǎn),然后在它響應(yīng)瀏覽器請(qǐng)求并向提出請(qǐng)求的瀏覽器傳輸 .html 和 .jpg 文件的同時(shí),對(duì)該程序進(jìn)行監(jiān)視。清單 10?介紹了如何完成該任務(wù)。
清單 10. 設(shè)置斷點(diǎn)
(gdb) break 188 Breakpoint 1 at 0x8048e70: file nweb.c, line 188. (gdb) commands 1 Type commands for when breakpoint 1 is hit, one per line. End with a line saying just "end". >continue >end (gdb) c Continuing.此時(shí),GDB 調(diào)試工具已設(shè)置為在 nweb 服務(wù)器接受?瀏覽器請(qǐng)求處進(jìn)行中斷,該調(diào)試工具將僅僅顯示相應(yīng)的請(qǐng)求并繼續(xù)處理其他的請(qǐng)求,而不會(huì)中斷正在運(yùn)行的程序。刷新幾次瀏覽器中的?http://localhost:9090/?頁面,可以觀察到,GDB 調(diào)試工具顯示了斷點(diǎn)并繼續(xù)運(yùn)行。
在刷新瀏覽器頁面的同時(shí),您應(yīng)該看到如清單 11?所示的斷點(diǎn)信息,在 GDB 調(diào)試工具 xterm 中滾動(dòng)輸出。與 strace 相同,您可以按下 Ctrl+C 來停止對(duì) nweb 服務(wù)器的調(diào)試。在停止了跟蹤操作之后,您可以輸入?quit?命令以退出 GDB 調(diào)試工具。
清單 11. GDB 調(diào)試工具 xterm 中的斷點(diǎn)信息
Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188 188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0) Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188 188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0) Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188 188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0) Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188 188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0) Program received signal SIGINT, Interrupt. 0xffffe410 in ?? () (gdb) quit The program is running. Quit anyway (and detach it)? (y or n) y Detaching from program: /home/bill/src/nweb/nweb, process 4009 $請(qǐng)注意,您正告訴 GDB 調(diào)試工具停止對(duì)一個(gè)仍在內(nèi)存中活動(dòng)的程序的調(diào)試。即使是在退出了調(diào)試工具之后,您還可以刷新瀏覽器頁面,并將看到 nweb 仍在運(yùn)行。可以輸入?kill 4009?命令來停止該程序,或者在您退出會(huì)話時(shí),該頁面將會(huì)消失。
和平常一樣,您可以通過其 man 和 info 頁面來了解各種各樣的工具,如 strace 和 GDB 調(diào)試工具。請(qǐng)確保使用 UNIX 為您提供的這些工具!
回頁首
了解盡可能多的信息
了解關(guān)于您所使用的計(jì)算機(jī)的盡可能多的信息,絕不是件壞事,并且還可以從這個(gè)過程中獲得樂趣。實(shí)際上,UNIX 通過提供各種工具,如 strace 和 GDB 調(diào)試工具以及包含在相應(yīng)的 man 和 info 頁面中的大量的信息,鼓勵(lì)您對(duì)系統(tǒng)進(jìn)行研究和學(xué)習(xí)。計(jì)算機(jī)是人類智慧的延伸,并且我們對(duì)其了解得越多,它們將變得越有用。
總結(jié)
以上是生活随笔為你收集整理的使用 Strace 和 GDB 调试工具的乐趣的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 25个CSS3 渐变和动画效果教程
- 下一篇: ASP中随机函数Randomize的使用