• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 10, 文章 - 1, 評論 - 9, 引用 - 0
            數(shù)據(jù)加載中……

            Nginx、FCGI的安裝與配置

            Nginx介紹

            Nginx發(fā)音為[engine x],是由俄羅斯人Igor Sysoev建立的項目,基于BSD許可。據(jù)說他當初是F5的成員之一,英文主頁:http://nginx.net。俄羅斯的一些大網(wǎng)站已經(jīng)使用它超過兩年多了,一直表現(xiàn)不凡。

            在這里,需要說明一下,由于Nginx的配置文件中用到正則,所以需要pcre模塊的支持。

             

            現(xiàn)在以/home/qa/local/nginx/install_src為工作目錄:

            創(chuàng)建pcrenginxsrc目錄

            mkdir nginx pcre

            下載pcre

            cd pcre

            wget http://sourceforge.net/projects/pcre/files/pcre/8.12/pcre-8.12.zip/download

            解壓pcre-8.12.zip

            unzip pcre-8.12.zip

            下載nginx

            cd  ../nginx/

            wget http://nginx.org/download/nginx-1.0.5.tar.gz

            解壓nginx

            tar xzvf nginx-1.0.5.tar.gz

            編譯nginx

            ./configure --prefix=/home/qa/local/nginx --with-pcre=/home/qa/local/nginx/install_src/pcre/pcre-8.12 --with-http_stub_status_module

             

            make

            make install

            上面就是nginx的安裝過程

            配置過程如下:

            Nginx配置文件的路徑為:/home/qa/local/nginx/conf/nginx.conf

            修改

            Server

            {

            Lesten 9091;#監(jiān)聽9091端口

            }

             

            location ~ \.fcgi$ {

            root           html;

            fastcgi_pass   127.0.0.1:9092;

            fastcgi_index index.html;

            fastcgi_param SCRIPT_FILENAME /home/qa/local/nginx/html$fastcgi_script_name;

            include        fastcgi_params;

            }

            添加以上的配置用以支持fcgi的任務(wù)轉(zhuǎn)發(fā)

            FCGI的安裝以及配置

            安裝fcgi

            ./configure  –prefix=/home/qa/local/lib

            Make

            Make install

             

            安裝cgicc

            ./configure --prefix=/home/qa/local/lib

            Make

            Make install

            安裝spawn-fcgi

            ./configure –prefix=/home/qa/local/sbin

            Make

            Make install

             

            fcgilib添加入環(huán)境變量

            export LD_INCLUDE_PATH=$HOME/local/lib/include

            export LD_LIBRARY_PATH=$HOME/local/lib/lib

             

            修改環(huán)境變量

            Vi ~/.bash_profile

            PATH=$PATH:$HOME/bin:$HOME/local/sbin

            加上后面加紅的一句,注意在~/.bashrc中也要做相應(yīng)的修改。

            如下:

            export  PATH=$PATH:$HOME/bin: $HOME /local/sbin

             

            Nginx的啟動:

            ./nginx –c ../conf/nginx.conf

             

            Nginx的關(guān)閉

            Killall nginx

             

            孵化search.fcgi

            Spawn-fcgi –a 127.0.0.1 –p 9092 –f ./search.fcgi

            posted @ 2011-09-25 13:13 Roger 閱讀(5117) | 評論 (2)編輯 收藏

            Valgrind 使用簡單說明(轉(zhuǎn)載)

            Valgrind 介紹

            Valgrind是一個GPL的軟件,用于Linux(For x86, amd64 and ppc32)程序的內(nèi)存調(diào)試和代碼剖析。你可以在它的環(huán)境中運行你的程序來監(jiān)視內(nèi)存的使用情況,比如C 語言中的malloc和free或者 C++中的new和 delete。使用Valgrind的工具包,你可以自動的檢測許多內(nèi)存管理和線程的bug,避免花費太多的時間在bug尋找上,使得你的程序更加穩(wěn)固。

            Valgrind的主要功能

            Valgrind工具包包含多個工具,如Memcheck,Cachegrind,Helgrind, Callgrind,Massif。下面分別介紹個工具的作用:

            Memcheck 工具主要檢查下面的程序錯誤:

            • 使用未初始化的內(nèi)存 (Use of uninitialised memory)
            • 使用已經(jīng)釋放了的內(nèi)存 (Reading/writing memory after it has been free’d)
            • 使用超過 malloc分配的內(nèi)存空間(Reading/writing off the end of malloc’d blocks)
            • 對堆棧的非法訪問 (Reading/writing inappropriate areas on the stack)
            • 申請的空間是否有釋放 (Memory leaks – where pointers to malloc’d blocks are lost forever)
            • malloc/free/new/delete申請和釋放內(nèi)存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
            • src和dst的重疊(Overlapping src and dst pointers in memcpy() and related functions)

            Callgrind

            Callgrind收集程序運行時的一些數(shù)據(jù),函數(shù)調(diào)用關(guān)系等信息,還可以有選擇地進行cache 模擬。在運行結(jié)束時,它會把分析數(shù)據(jù)寫入一個文件。callgrind_annotate可以把這個文件的內(nèi)容轉(zhuǎn)化成可讀的形式。

            Cachegrind

            它模擬 CPU中的一級緩存I1,D1和L2二級緩存,能夠精確地指出程序中 cache的丟失和命中。如果需要,它還能夠為我們提供cache丟失次數(shù),內(nèi)存引用次數(shù),以及每行代碼,每個函數(shù),每個模塊,整個程序產(chǎn)生的指令數(shù)。這對優(yōu)化程序有很大的幫助。

            Helgrind

            它主要用來檢查多線程程序中出現(xiàn)的競爭問題。Helgrind 尋找內(nèi)存中被多個線程訪問,而又沒有一貫加鎖的區(qū)域,這些區(qū)域往往是線程之間失去同步的地方,而且會導(dǎo)致難以發(fā)掘的錯誤。Helgrind實現(xiàn)了名為” Eraser” 的競爭檢測算法,并做了進一步改進,減少了報告錯誤的次數(shù)。

            Massif

            堆棧分析器,它能測量程序在堆棧中使用了多少內(nèi)存,告訴我們堆塊,堆管理塊和棧的大小。Massif能幫助我們減少內(nèi)存的使用,在帶有虛擬內(nèi)存的現(xiàn)代系統(tǒng)中,它還能夠加速我們程序的運行,減少程序停留在交換區(qū)中的幾率。

            Valgrind 安裝

            1、 到www.valgrind.org下載最新版valgrind-3.2.3.tar.bz2
            2、 解壓安裝包:tar –jxvf valgrind-3.2.3.tar.bz2
            3、 解壓后生成目錄valgrind-3.2.3
            4、 cd valgrind-3.2.3
            5、 ./configure
            6、 Make;make install

            Valgrind 使用

            用法: valgrind [options] prog-and-args [options]: 常用選項,適用于所有Valgrind工具

            1. -tool=<name> 最常用的選項。運行 valgrind中名為toolname的工具。默認memcheck。
            2. h –help 顯示幫助信息。
            3. -version 顯示valgrind內(nèi)核的版本,每個工具都有各自的版本。
            4. q –quiet 安靜地運行,只打印錯誤信息。
            5. v –verbose 更詳細的信息, 增加錯誤數(shù)統(tǒng)計。
            6. -trace-children=no|yes 跟蹤子線程? [no]
            7. -track-fds=no|yes 跟蹤打開的文件描述?[no]
            8. -time-stamp=no|yes 增加時間戳到LOG信息? [no]
            9. -log-fd=<number> 輸出LOG到描述符文件 [2=stderr]
            10. -log-file=<file> 將輸出的信息寫入到filename.PID的文件里,PID是運行程序的進行ID
            11. -log-file-exactly=<file> 輸出LOG信息到 file
            12. -log-file-qualifier=<VAR> 取得環(huán)境變量的值來做為輸出信息的文件名。 [none]
            13. -log-socket=ipaddr:port 輸出LOG到socket ,ipaddr:port

            LOG信息輸出

            1. -xml=yes 將信息以xml格式輸出,只有memcheck可用
            2. -num-callers=<number> show <number> callers in stack traces [12]
            3. -error-limit=no|yes 如果太多錯誤,則停止顯示新錯誤? [yes]
            4. -error-exitcode=<number> 如果發(fā)現(xiàn)錯誤則返回錯誤代碼 [0=disable]
            5. -db-attach=no|yes 當出現(xiàn)錯誤,valgrind會自動啟動調(diào)試器gdb。[no]
            6. -db-command=<command> 啟動調(diào)試器的命令行選項[gdb -nw %f %p]

            適用于Memcheck工具的相關(guān)選項:

            1. -leak-check=no|summary|full 要求對leak給出詳細信息? [summary]
            2. -leak-resolution=low|med|high how much bt merging in leak check [low]
            3. -show-reachable=no|yes show reachable blocks in leak check? [no]

            Valgrind 使用舉例

            下面是一段有問題的C程序代碼test.c

            #include <stdlib.h>
            void f(void)
            {
            int* x = malloc(10 * sizeof(int));
            x[10] = 0; //問題1: 數(shù)組下標越界
            } //問題2: 內(nèi)存沒有釋放
            int main(void)
            {
            f();
            return 0;
            }
            1、 編譯程序test.c
            gcc -Wall test.c -g -o test
            2、 使用Valgrind檢查程序BUG
            valgrind --tool=memcheck --leak-check=full ./test
            3、 分析輸出的調(diào)試信息
            ==3908== Memcheck, a memory error detector.
            ==3908== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
            ==3908== Using LibVEX rev 1732, a library for dynamic binary translation.
            ==3908== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
            ==3908== Using valgrind-3.2.3, a dynamic binary instrumentation framework.
            ==3908== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
            ==3908== For more details, rerun with: -v
            ==3908==
            --3908-- DWARF2 CFI reader: unhandled CFI instruction 0:50
            --3908-- DWARF2 CFI reader: unhandled CFI instruction 0:50
            /*數(shù)組越界錯誤*/
            ==3908== Invalid write of size 4
            ==3908== at 0x8048384: f (test.c:6)
            ==3908== by 0x80483AC: main (test.c:11)
            ==3908== Address 0x400C050 is 0 bytes after a block of size 40 alloc'd
            ==3908== at 0x40046F2: malloc (vg_replace_malloc.c:149)
            ==3908== by 0x8048377: f (test.c:5)
            ==3908== by 0x80483AC: main (test.c:11)
            ==3908==
            ==3908== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 14 from 1)
            ==3908== malloc/free: in use at exit: 40 bytes in 1 blocks.
            ==3908== malloc/free: 1 allocs, 0 frees, 40 bytes allocated.
            ==3908== For counts of detected errors, rerun with: -v
            ==3908== searching for pointers to 1 not-freed blocks.
            ==3908== checked 59,124 bytes.
            ==3908==
            ==3908==
            /*有內(nèi)存空間沒有釋放*/
            ==3908== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
            ==3908== at 0x40046F2: malloc (vg_replace_malloc.c:149)
            ==3908== by 0x8048377: f (test.c:5)
            ==3908== by 0x80483AC: main (test.c:11)
            ==3908==
            ==3908== LEAK SUMMARY:
            ==3908== definitely lost: 40 bytes in 1 blocks.
            ==3908== possibly lost: 0 bytes in 0 blocks.
            ==3908== still reachable: 0 bytes in 0 blocks.
            ==3908== suppressed: 0 bytes in 0 blocks.

            posted @ 2011-08-05 09:30 Roger 閱讀(530) | 評論 (0)編輯 收藏

            Linux on Power 上的調(diào)試工具和技術(shù)(轉(zhuǎn)載)

            原文地址:https://www.ibm.com/developerworks/cn/linux/l-pow-debug/


            簡介

            調(diào)試程序有很多方法,例如向屏幕上打印消息,使用調(diào)試器,或者只需仔細考慮程序如何運行,并對問題進行有根有據(jù)的猜測。

            在修復(fù) bug 之前,首先要確定在源程序中的位置。例如,當一個程序產(chǎn)生崩潰或生成核心轉(zhuǎn)儲(core dump)時,您就需要了解是哪行代碼發(fā)生了崩潰。在找到有問題的代碼行之后,就可以確定這個函數(shù)中變量的值,函數(shù)是如何調(diào)用的,更具體點說,為什么會發(fā)生這種錯誤。使用調(diào)試器查找這些信息非常簡單。

            本文將簡要介紹幾種用于修復(fù)一些很難通過可視化地檢查代碼而發(fā)現(xiàn)的 bug 的技術(shù),并闡述了如何使用在 Linux on Power 架構(gòu)上可用的工具。


            調(diào)試內(nèi)存問題的工具和技術(shù)

            動態(tài)內(nèi)存分配看起來似乎非常簡單:您可以根據(jù)需要分配內(nèi)存 —— 使用 malloc() 或其變種 —— 并在不需要時釋放這些內(nèi)存。實際上,內(nèi)存管理的問題是軟件中最為常見的 bug,因為通常在程序啟動時這些問題并不明顯。例如,程序中的內(nèi)存泄漏可能開始并不為人注意,直到經(jīng)過多天甚至幾個月的運行才會被發(fā)現(xiàn)。接下來的幾節(jié)將簡要介紹如何使用流行的調(diào)試器 Valgrind 來發(fā)現(xiàn)并調(diào)試這些最常見的內(nèi)存 bug。

            在開始使用任何調(diào)試工具之前,請考慮這個工具是否對重新編譯應(yīng)用程序有益,是否可以支持具有調(diào)試信息的庫(-g 選項)。如果沒有啟用調(diào)試信息,調(diào)試工具可以做的最好的事情也不過是猜測一段特定的代碼是屬于哪個函數(shù)的。這使得錯誤消息和概要分析輸出幾乎沒有什么用處。使用 -g 選項,您就有可能獲得一些信息來直接指出相關(guān)的代碼行。

            Valgrind

            Valgrind 已經(jīng)在 Linux 應(yīng)用程序開發(fā)社區(qū)中廣泛用來調(diào)試應(yīng)用程序。它尤其擅長發(fā)現(xiàn)內(nèi)存管理的問題。它可以檢查程序運行時的內(nèi)存泄漏問題。這個工具目前正由 Julian Seward 進行開發(fā),并由 Paul Mackerras 移植到了 Power 架構(gòu)上。

            要安裝 Valgrind,請從 Valgrind 的 Web 站點上下載源代碼(參閱 參考資料)。切換到 Valgrind 目錄,并執(zhí)行下面的命令:

            # make
            # make check
            # make install
            

            Valgrind 的錯誤報告

            Valgrind 的輸出格式如下:


            清單 1. Valgrind 的輸出消息
                            
            # valgrind du –x –s
            .
            .
            ==29404==  Address 0x1189AD84 is 0 bytes after a block of size 12 alloc'd
            ==29404==    at 0xFFB9964: malloc (vg_replace_malloc.c:130)
            ==29404==    by 0xFEE1AD0: strdup (in /lib/tls/libc.so.6)
            ==29404==    by 0xFE94D30: setlocale (in /lib/tls/libc.so.6)
            ==29404==    by 0x10001414: main (in /usr/bin/du)
            

            ==29404== 是進程的 ID。消息 Address 0x1189AD84 is 0 bytes after a block of size 12 alloc'd 說明在這個 12 字節(jié)的數(shù)組后面沒有存儲空間了。第二行以及后續(xù)幾行說明內(nèi)存是在 130 行(vg_replace_malloc.c)的 strdup() 程序中進行分配的。strdup() 是在 libc.so.6 庫的 setlocale() 中調(diào)用的;main() 調(diào)用了 setlocale()

            未初始化的內(nèi)存

            最為常見的一個 bug 是程序使用了未初始化的內(nèi)存。未初始化的數(shù)據(jù)可能來源于:

            • 未經(jīng)初始化的變量
            • malloc 函數(shù)所分配的數(shù)據(jù),在寫入值之前使用了

            下面這個例子使用了一個未初始化的數(shù)組:


            清單 2. 使用未初始化的內(nèi)存
                            
                  2 {
                  3         int i[5];
                  4 
                  5         if (i[0] == 0)
                  6                 i[1]=1;
                  7         return 0;
                  8 }
            

            在這個例子中,整數(shù)數(shù)組 i[5] 沒有進行初始化;因此,i[0] 包含的是一個隨機數(shù)。因此使用 i[0] 的值來判斷一個條件分支就會導(dǎo)致不可預(yù)期的問題。Valgrind 可以很容易捕獲這種錯誤條件。當您使用 Valgrind 運行這個程序時,就會接收到下面的消息:


            清單 3. Valgrind 的輸出消息
                            
            # gcc –g –o test1 test1.c
            # valgrind ./test1
            .
            .
            ==31363== 
            ==31363== Conditional jump or move depends on uninitialised value(s)
            ==31363==    at 0x1000041C: main (test1.c:5)
            ==31363== 
            ==31363== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 7 from 1)
            ==31363== malloc/free: in use at exit: 0 bytes in 0 blocks.
            ==31363== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
            ==31363== For counts of detected errors, rerun with: -v
            ==31363== No malloc'd blocks -- no leaks are possible.
            

            Valgrind 的輸出說明,有一個條件分支依賴于文件 test1.c 中第 5 行中的一個未初始化的變量。

            內(nèi)存泄漏

            內(nèi)存泄漏是另外一個常見的問題,也是很多程序中最難判斷的問題。內(nèi)存泄漏的主要表現(xiàn)為:當程序連續(xù)運行時,與程序相關(guān)的內(nèi)存(或堆)變得越來越大。結(jié)果是,當這個程序所消耗的內(nèi)存達到系統(tǒng)的上限時,就會自己崩潰;或者會出現(xiàn)更嚴重的情況:掛起或?qū)е孪到y(tǒng)崩潰。下面是一個有內(nèi)存泄漏 bug 的示例程序:


            清單 4. 內(nèi)存泄漏示例
                            
                  1 int main(void)
                  2 {
                  3         char *p1;
                  4         char *p2;
                  5 
                  6         p1 = (char *) malloc(512);
                  7         p2 = (char *) malloc(512);
                  8 
                  9         p1=p2;
                 10 
                 11         free(p1);
                 12         free(p2);
                 13 }
            

            上面的代碼分別給字符指針 p1 和 p2 分配了兩個 512 字節(jié)的內(nèi)存塊,然后將指向第一個內(nèi)存塊的指針設(shè)置為指向第二個內(nèi)存塊。結(jié)果是,第二個內(nèi)存塊的地址丟失了,并導(dǎo)致內(nèi)存泄漏。在使用 Valgrind 運行這個程序時,會返回如下的消息:


            清單 5. Valgrind 的輸出消息
                            
            # gcc –g –o test2 test2.c
            # valgrind ./test2
            .
            .
            ==31468== Invalid free() / delete / delete[]
            ==31468==    at 0xFFB9FF0: free (vg_replace_malloc.c:152)
            ==31468==    by 0x100004B0: main (test2.c:12)
            ==31468== Address 0x11899258 is 0 bytes inside a block of size 512 free'd
            ==31468==    at 0xFFB9FF0: free (vg_replace_malloc.c:152)
            ==31468==    by 0x100004A4: main (test2.c:11)
            ==31468== 
            ==31468== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 7 from 1)
            ==31468== malloc/free: in use at exit: 512 bytes in 1 blocks.
            ==31468== malloc/free: 2 allocs, 2 frees, 1024 bytes allocated.
            ==31468== For counts of detected errors, rerun with: -v
            ==31468== searching for pointers to 1 not-freed blocks.
            ==31468== checked 167936 bytes.
            ==31468== 
            ==31468== LEAK SUMMARY:
            ==31468==    definitely lost: 512 bytes in 1 blocks.
            ==31468==      possibly lost: 0 bytes in 0 blocks.
            ==31468==    still reachable: 0 bytes in 0 blocks.
            ==31468==         suppressed: 0 bytes in 0 blocks.
            ==31468== Use --leak-check=full to see details of leaked memory.
            

            正如您可以看到的一樣,Valgrind 報告說這個程序中有 512 字節(jié)的內(nèi)存丟失了。

            非法寫/讀

            這種情況發(fā)生在程序試圖對一個不屬于程序本身的內(nèi)存地址進行讀寫時。在有些系統(tǒng)上,在發(fā)生這種錯誤時,程序會異常結(jié)束,并產(chǎn)生一個段錯誤。下面這個例子就是一個常見的 bug,它試圖讀寫一個超出數(shù)組邊界的元素。


            清單 6. 非法讀寫
                            
                  1 int main() {
                  2         int i, *iw, *ir;
                  3 
                  4         iw = (int *)malloc(10*sizeof(int));
                  5         ir = (int *)malloc(10*sizeof(int));
                  6 
                  7 
                  8         for (i=0; i<11; i++)
                  9                 iw[i] = i;
                 10 
                 11         for (i=0; i<11; i++)
                 12                 ir[i] = iw[i];
                 13 
                 14         free(iw);
                 15         free(ir);
                 16 } 
            

            從這個程序中我們可以看出,對于 iw[10]ir[10] 的訪問都是非法的,因為 iwir 都只有 10 個元素,分別是從 0 到 9。請注意 int iw[10 ]iw = (int *)malloc(10*sizeof(int)) 是等效的 —— 它們都是用來給一個整數(shù)數(shù)組 iw 分配 10 個元素。

            當您使用 Valgrind 運行這個程序時,會返回如下的消息:


            清單 7. Valgrind 的輸出消息
                            
            # gcc –g –o test3 test3.c
            # valgrind ./test3
            .
            .
            ==31522== Invalid write of size 4
            ==31522==    at 0x100004C0: main (test3.c:9)
            ==31522==  Address 0x11899050 is 0 bytes after a block of size 40 alloc'd
            ==31522==    at 0xFFB9964: malloc (vg_replace_malloc.c:130)
            ==31522==    by 0x10000474: main (test10.c:4)
            ==31522== 
            ==31522== Invalid read of size 4
            ==31522==    at 0x1000050C: main (test3.c:12)
            ==31522==  Address 0x11899050 is 0 bytes after a block of size 40 alloc'd
            ==31522==    at 0xFFB9964: malloc (vg_replace_malloc.c:130)
            ==31522==    by 0x10000474: main (test10.c:4)
            ==31522== 
            ==31522== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 7 from 1)
            ==31522== malloc/free: in use at exit: 0 bytes in 0 blocks.
            ==31522== malloc/free: 2 allocs, 2 frees, 84 bytes allocated.
            ==31522== For counts of detected errors, rerun with: -v
            ==31522== No malloc'd blocks -- no leaks are possible.
            

            在 test3.c 的第 9 行發(fā)現(xiàn)一個非法的 4 字節(jié)寫操作,在第 12 行發(fā)現(xiàn)一個非法的 4 字節(jié)讀操作。

            Valgrind 也可以幫助判斷內(nèi)存誤用的問題,例如:

            • 讀/寫已經(jīng)釋放的內(nèi)存
            • C++ 環(huán)境中錯誤地使用 malloc/new 與 free/delete 的配對

            下面這個列表介紹了 POWER 架構(gòu)上 Valgrind 的狀態(tài):

            • memcheck 和 addrcheck 工具都可以很好地工作。然而,其他工具還沒有進行大量的測試。另外,Helgrind (一個數(shù)據(jù)競爭的檢測程序)在 POWER 上尚不能使用。
            • 所有的 32 位 PowerPC? 用戶模式的指令都可以支持,除了兩條非常少用的指令:lswx 和 stswx。具體來說,所有的浮點和 Altivec(VMX)指令都可以支持。
            • Valgrind 可以在 32 位或 64 位 PowerPC/Linux 內(nèi)核上工作,但是只能用于 32 位的可執(zhí)行程序。

            有關(guān) Valgrind 內(nèi)存調(diào)試的更多信息,請訪問 Valgrind HOW TO 站點。還可以參閱 Steve Best 的“Debugging Memory Problems”(Linux Magazine,2003 年 5 月)。參考資料 中有它們的鏈接

            除了 Valgrind 之外,還可以使用其他幾個內(nèi)存調(diào)試工具;例如,Memwatch 和 Electric Fence。


            調(diào)試其他程序問題的工具和技術(shù)

            除了內(nèi)存 bug 之外,開發(fā)人員通常還會碰到程序雖然能夠成功編譯,但是在運行時卻會產(chǎn)生內(nèi)核轉(zhuǎn)儲或段錯誤的問題。有時在程序完成之后,程序的輸出可能與所期望或設(shè)計的不同。在這兩種情況中,可能代碼中存在您認為正確而實際上錯誤的情況。接下來的幾節(jié)中介紹的調(diào)試器將幫助您找到這些情況的原因。

            GNU 項目調(diào)試器

            GDB(GNU 項目調(diào)試器)可以讓您了解程序在執(zhí)行時“內(nèi)部” 究竟在干些什么,以及在程序發(fā)生崩潰的瞬間正在做什么。

            GDB 做以下 4 件主要的事情來幫助您捕獲程序中的 bug:

            • 在程序啟動之前指定一些可以影響程序行為的變量或條件
            • 在某個指定的地方或條件下暫停程序
            • 在程序停止時檢查已經(jīng)發(fā)生了什么
            • 在程序執(zhí)行過程中修改程序中的變量或條件,這樣就可以體驗修復(fù)一個 bug 的成果,并繼續(xù)了解其他 bug

            要調(diào)試的程序可以是使用 C、C++、Pascal、Objective-C 以及其他很多語言編寫的。GDB 的二進制文件名是 gdb。

            gdb 中有很多命令。使用 help 命令可以列出所有的命令,以及關(guān)于如何使用這些命令的介紹。下表給出了最常用的 GDB 命令。


            表 1. gdb 中最常用的命令
            命令 說明 例子
            help 顯示命令類別 help - 顯示命令類別
            help breakpoints - 顯示屬于 breakpoints 類別的命令
            help break - 顯示 break 命令的解釋
            run 啟動所調(diào)試的程序 ?
            kill 終止正在調(diào)試的程序的執(zhí)行 通常這會在要執(zhí)行的代碼行已經(jīng)超過了您想要調(diào)試的代碼時使用。執(zhí)行 kill 會重置斷點,并從頭再次運行這個程序
            cont 所調(diào)試的程序運行到一個斷點、異常或單步之后,繼續(xù)執(zhí)行 ?
            info break 顯示當前的斷點或觀察點 ?
            break 在指定的行或函數(shù)處設(shè)置斷點 break 93 if i=8 - 當變量 i 等于 8 時,在第 93 行停止程序執(zhí)行
            Step 單步執(zhí)行程序,直到它到達一個不同的源代碼行。您可以使用 s 來代表 step 命令 ?
            Next 與 step 命令類似,只是它不會“單步跟蹤到”子例程中 ?
            print 打印一個變量或表達式的值 print pointer - 打印變量指針的內(nèi)容
            print *pointer - 打印指針所指向的數(shù)據(jù)結(jié)構(gòu)的內(nèi)容
            delete 刪除某些斷點或自動顯示表達式 delete 1 - 刪除斷點 1。斷點可以通過 info break 來顯示
            watch 為一個表達式設(shè)置一個觀察點。當表達式的值發(fā)生變化時,這個觀察點就會暫停程序的執(zhí)行 ?
            where 打印所有堆棧幀的棧信息 where - 不使用參數(shù),輸出當前線程的堆棧信息
            where all - 輸出當前線程組中所有線程的堆棧信息
            where threadindex - 輸出指定線程的堆棧信息
            attach 開始查看一個已經(jīng)運行的進程 attach <process_id> - 附加到進程 process_id 上。process_id 可以使用 ps 命令找到
            info thread 顯示當前正在運行的線程 ?
            thread apply threadno command 對一個線程運行 gdb 命令 thread apply 3 where - 對線程 3 運行 where 命令
            Thread threadno 選擇一個線程作為當前線程 ?

            如果一個程序崩潰了,并生成了一個 core 文件,您可以查看 core 文件來判斷進程結(jié)束時的狀態(tài)。使用下面的命令啟動 gdb:

            # gdb programname corefilename 

            要調(diào)試一個 core 文件,您需要可執(zhí)行程序、源代碼文件以及 core 文件。要對一個 core 文件啟動 gdb,請使用 -c 選項:

            # gdb -c core programname 

            gdb 會顯示是哪行代碼導(dǎo)致這個程序產(chǎn)生了核心轉(zhuǎn)儲。

            默認情況下,核心轉(zhuǎn)儲在 Novell 的 SUSE LINUX Enterprise Server 9(SLES 9)和 Red Hat? Enterprise Linux Advanced Server(RHEL AS 4)上都是禁用的。要啟用核心轉(zhuǎn)儲,請以 root 用戶的身份在命令行中執(zhí)行 ulimit –c unlimited

            清單 8 中的例子闡述了如何使用 gdb 來定位程序中的 bug。清單 8 是一段包含 bug 的 C++ 代碼。

            清單 8 中的 C++ 程序試圖構(gòu)建 10 個鏈接在一起的數(shù)字框(number box),例如:


            圖 1. 一個包含 10 個鏈接在一起的數(shù)字框的列表

            然后試圖從這個列表中逐個刪除數(shù)字框。

            編譯并運行這個程序,如下所示:


            清單 9. 編譯并運行這個程序
                            
            # g++ -g -o gdbtest1 gdbtest1.cpp
            # ./gdbtest1
            Number Box "0" created
            Number Box "1" created
            Number Box "2" created
            Number Box "3" created
            Number Box "4" created
            Number Box "5" created
            Number Box "6" created
            Number Box "7" created
            Number Box "8" created
            Number Box "9" created
            list created
            Number Box "9" deleted
            Segmentation fault
            

            正如您可以看到的一樣,這個程序會導(dǎo)致段錯誤。調(diào)用 gdb 來看一下這個問題,如下所示:


            清單 10. 調(diào)用 gdb
                            
            # gdb ./gdbtest1
            GNU gdb 6.2.1
            Copyright 2004 Free Software Foundation, Inc.
            GDB is free software, covered by the GNU General Public License, and you 
            are welcome to change it and/or distribute copies of it under certain 
            conditions.
            Type "show copying" to see the conditions.
            There is absolutely no warranty for GDB.  Type "show warranty" for 
            details.
            This GDB was configured as "ppc-suse-linux"...Using host libthread_db 
            library "/lib/tls/libthread_db.so.1".
            (gdb)
            

            您知道段錯誤是在數(shù)字框 "9" 被刪除之后發(fā)生的。執(zhí)行 runwhere 命令來精確定位段錯誤發(fā)生在程序中的什么位置。


            清單 11. 執(zhí)行 run 和 where 命令
                            
            (gdb) run
            Starting program: /root/test/gdbtest1 
            Number Box "0" created
            Number Box "1" created
            Number Box "2" created
            Number Box "3" created
            Number Box "4" created
            Number Box "5" created
            Number Box "6" created
            Number Box "7" created
            Number Box "8" created
            Number Box "9" created
            list created
            Number Box "9" deleted
            Program received signal SIGSEGV, Segmentation fault.
            0x10000f74 in NumBox<int>::GetNext (this=0x0) at gdbtest1.cpp:14
            14              NumBox<T>*GetNext() const { return Next; }
            (gdb) where
            #0  0x10000f74 in NumBox<int>::GetNext (this=0x0) at gdbtest1.cpp:14
            #1  0x10000d10 in NumChain<int>::RemoveBox (this=0x10012008, 
            
            item_to_remove=@0xffffe200) at gdbtest1.cpp:63 #2 0x10000978 in main (argc=1, argv=0xffffe554) at gdbtest1.cpp:94 (gdb)

            跟蹤信息顯示這個程序在第 14 行 NumBox<int>::GetNext (this=0x0) 接收到一個段錯誤。這個數(shù)字框上 Next 指針的地址是 0x0,這對于一個數(shù)字框來說是一個無效的地址。從上面的跟蹤信息可以看出,GetNext 函數(shù)是由 63 行調(diào)用的。看一下在 gdbtest1.cpp 的 63 行附近發(fā)生了什么:


            清單 12. gdbtest1.cpp
                            
                 54                       } else {
                 55                               temp->SetNext (current->GetNext());
                 56                               delete temp;
                 57                               temp = 0;
                 58                               return 0;
                 59                       }
                 60               }
                 61               current = 0;
                 62               temp = current;
                 63               current = current->GetNext();
                 64       }
                 65 
                 66       return -1;
            

            第 61 行 current=0 將這個指針設(shè)置為一個無效的地址,這正是產(chǎn)生段錯誤的根源。注釋掉第 61 行,將其保存為 gdbtest2.cpp,然后編譯并重新運行。


            清單 13. 再次運行程序(gdbtest2.cpp)
                            
            # g++ -g -o gdbtest2 gdbtest2.cpp
            # ./gdbtest2
            Number Box "0" created
            Number Box "1" created
            Number Box "2" created
            Number Box "3" created
            Number Box "4" created
            Number Box "5" created
            Number Box "6" created
            Number Box "7" created
            Number Box "8" created
            Number Box "9" created
            list created
            Number Box "9" deleted
            Number Box "0" deleted
            

            這個程序現(xiàn)在可以成功完成而不會出現(xiàn)段錯誤了。然而,結(jié)果并不像我們預(yù)期的一樣:程序在刪除 Number Box "9"之后刪除了 Number Box "0",而不像我們期望的一樣刪除 Number Box "8,"。使用 gdb 再次來看一下。


            清單 14. 再次使用 gdb 進行查看
                            
            # gdb ./gdbtest2
            GNU gdb 6.2.1
            Copyright 2004 Free Software Foundation, Inc.
            GDB is free software, covered by the GNU General Public License, and you 
            are welcome to change it and/or distribute copies of it under certain 
            conditions.
            Type "show copying" to see the conditions.
            There is absolutely no warranty for GDB.  Type "show warranty" for 
            details.
            This GDB was configured as "ppc-suse-linux"...Using host libthread_db 
            library "/lib/tls/libthread_db.so.1".
            (gdb) break 94 if i==8
            Breakpoint 1 at 0x10000968: file gdbtest2.cpp, line 94.
            (gdb) run
            Starting program: /root/test/gdbtest2 
            Number Box "0" created
            Number Box "1" created
            Number Box "2" created
            Number Box "3" created
            Number Box "4" created
            Number Box "5" created
            Number Box "6" created
            Number Box "7" created
            Number Box "8" created
            Number Box "9" created
            list created
            Number Box "9" deleted
            Breakpoint 1, main (argc=1, argv=0xffffe554) at gdbtest2.cpp:94
            94                      list ->RemoveBox(i);
            

            您可能希望找出為什么這個程序刪除的是 Number Box 0,而不是 Number Box 8,因此需要在您認為程序會刪除 Number Box 8 的地方停止程序。設(shè)置這個斷點:break 94 if i==8,可以在 i 等于 8 時在第 94 行處停止程序。然后單步跟蹤RemoveBox() 函數(shù)中。


            清單 15. 單步跟蹤到 RemoveBox() 函數(shù)中
                            
            (gdb) s
            38                      NumBox<T> *temp = 0;  
            (gdb) s
            40                      while (current != 0) {
            (gdb) print pointer
            $1 = (NumBox<int> *) 0x100120a8
             (gdb) print *pointer
            $2 = {Num = 0, Next = 0x0}
            (gdb)
            

            指針早已指向了 Number Box "0",因此這個 bug 可能就存在于程序刪除 Number Box "9" 的地方。要在 gdb 中重新啟動這個程序,請使用 kill 刪除原來的斷點,然后添加一個 i 等于 9 時的新斷點,然后再次運行這個程序。


            清單 16. 在 gdb 中重新啟動程序
                            
            (gdb) kill
            Kill the program being debugged? (y or n) y
            (gdb) info break
            Num Type           Disp Enb Address    What
            1   breakpoint     keep y   0x10000968 in main at gdbtest2.cpp:94
                    stop only if i == 8
                    breakpoint already hit 1 time
            (gdb) delete 1
            (gdb) break 94 if i==9
            Breakpoint 2 at 0x10000968: file gdbtest2.cpp, line 94.
            (gdb) run
            Starting program: /root/test/gdbtest2 
            Number Box "0" created
            Number Box "1" created
            Number Box "2" created
            Number Box "3" created
            Number Box "4" created
            Number Box "5" created
            Number Box "6" created
            Number Box "7" created
            Number Box "8" created
            Number Box "9" created
            list created
            Breakpoint 2, main (argc=1, argv=0xffffe554) at gdbtest2.cpp:94
            94                      list ->RemoveBox(i);
            (gdb)
            

            當這一次單步跟蹤 RemoveBox() 函數(shù)時,要特別注意 list->pointer 正在指向哪一個數(shù)字框,因為 bug 可能就在于 list->pointer 開始指向 Number Box "0" 的地方。請使用 display *pointer 命令來查看,這會自動顯示這個函數(shù)。


            清單 17. 使用 display *pointer 命令進行監(jiān)視
                            
            Breakpoint 2, main (argc=1, argv=0xffffe554) at gdbtest2.cpp:94
            94            list ->RemoveBox(i);
            (gdb) s
            NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200) 
            at gdbtest2.cpp:37 37 NumBox<T> *current = pointer; (gdb) display *pointer 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 38 NumBox<T> *temp = 0; 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 40 while (current != 0) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 41 if (current->GetValue() == item_to_remove) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s NumBox<int>::GetValue (this=0x100120a8) at gdbtest2.cpp:16 16 const T& GetValue () const { return Num; } (gdb) s NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200)
            at gdbtest2.cpp:42 42 if (temp == 0) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 44 if (current->GetNext() == 0) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s NumBox<int>::GetNext (this=0x100120a8) at gdbtest2.cpp:14 14 NumBox<T>*GetNext() const { return Next; } (gdb) s NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200)
            at gdbtest2.cpp:50 50 delete current; 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s ~NumBox (this=0x100120a8) at gdbtest2.cpp:10 10 std::cout << "Number Box " <<"\"" << GetValue()
            <<"\"" <<" deleted" << std::endl; (gdb) s NumBox<int>::GetValue (this=0x100120a8) at gdbtest2.cpp:16 16 const T& GetValue () const { return Num; } (gdb) s Number Box "9" deleted ~NumBox (this=0x100120a8) at gdbtest2.cpp:11 11 Next = 0; (gdb) s NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200)
            at gdbtest2.cpp:51 51 current = 0; 1: *this->pointer = {Num = 0, Next = 0x0} (gdb) s 53 return 0; 1: *this->pointer = {Num = 0, Next = 0x0} (gdb) s 0x10000d1c 66 return -1; 1: *this->pointer = {Num = 0, Next = 0x0}

            從上面的跟蹤過程中,您可以看到 list->pointer 在刪除 Number Box "9" 之后指向了 Number Box "0"。這個邏輯并不正確,因為在刪除 Number Box "9" 之后,list->pointer 應(yīng)該指向的是 Number Box "8"。現(xiàn)在非常顯然我們應(yīng)該在第 50 行之前添加一條語句 pointer = pointer->GetNext();,如下所示:


            清單 18. 在第 50 行之前添加一條 pointer = pointer->GetNext(); 語句
                            
                 49                     } else {
                 50                             pointer = pointer->GetNext();
                 51                             delete current;
                 52                             current = 0;
                 53                      }
                 54                      return 0;
            

            將新修改之后的程序保存為 gdbtest3.cpp,然后編譯并再次運行。


            清單 19. 再次運行程序(gdbtest3.cpp)
                            
            # g++ -g -o gdbtest3 gdbtest3.cpp
            # ./gdbtest3
            Number Box "0" created
            Number Box "1" created
            Number Box "2" created
            Number Box "3" created
            Number Box "4" created
            Number Box "5" created
            Number Box "6" created
            Number Box "7" created
            Number Box "8" created
            Number Box "9" created
            list created
            Number Box "9" deleted
            Number Box "8" deleted
            Number Box "7" deleted
            Number Box "6" deleted
            Number Box "5" deleted
            Number Box "4" deleted
            Number Box "3" deleted
            Number Box "2" deleted
            Number Box "1" deleted
            Number Box "0" deleted
            

            這才是我們期望的結(jié)果。

            多線程環(huán)境

            在 GDB 中有一些特殊的命令可以用于多線程應(yīng)用程序的調(diào)試。下面這個例子給出了一個死鎖情況,并介紹了如何使用這些命令來檢查多線程應(yīng)用程序中的問題:


            清單 20. 多線程的例子
                            
            #include <stdio.h>
            #include "pthread.h>
            pthread_mutex_t AccountA_mutex;
            pthread_mutex_t AccountB_mutex;
            struct BankAccount {
                 char account_name[1];
                 int balance;
            };
            struct BankAccount  accountA = {"A", 10000 };
            struct BankAccount  accountB = {"B", 20000 };
            void * transferAB (void* amount_ptr) {
                 int amount = *((int*)amount_ptr);
                 pthread_mutex_lock(&AccountA_mutex);
                 if (accountA.balance < amount)   {
                         printf("There is not enough memory in Account A!\n");
                         pthread_mutex_unlock(&AccountA_mutex);
                         pthread_exit((void *)1);
                 }
                 accountA.balance -=amount;
                 sleep(1);
                 pthread_mutex_lock(&AccountB_mutex);
                 accountB.balance +=amount;
                 pthread_mutex_unlock(&AccountA_mutex); 
                 pthread_mutex_unlock(&AccountB_mutex);
            }
            void * transferBA (void* amount_ptr) {
                 int amount = *((int*)amount_ptr);
                 pthread_mutex_lock(&AccountB_mutex);
                 if (accountB.balance < amount)   {
                         printf("There is not enough memory in Account B!\n");
                         pthread_mutex_unlock(&AccountB_mutex);
                         pthread_exit((void *)1);
                 }
                 accountB.balance -=amount;
                 sleep(1);
                 pthread_mutex_lock(&AccountA_mutex);
                 accountA.balance +=amount;
                 pthread_mutex_unlock(&AccountB_mutex);
                 pthread_mutex_unlock(&AccountA_mutex);
            }
            int main(int argc, char* argv[]) {
                 int             threadid[4];
                 pthread_t       pthread[4];
                 int             transfer_amount[4] = {100, 200, 300, 400};
                 int             final_balanceA, final_balanceB;
                 final_balanceA=accountA.balance-transfer_amount[0]-
            transfer_amount[1]+transfer_amount[2]+transfer_amount[3]; final_balanceB=accountB.balance+transfer_amount[0]
            +transfer_amount[1]-transfer_amount[2]-transfer_amount[3]; if (threadid[0] = pthread_create(&pthread[0], NULL, transferAB, (void*)&transfer_amount[0]) " 0) { perror("Thread #0 creation failed."); exit (1); } if (threadid[1] = pthread_create(&pthread[1], NULL, transferAB, (void*)&transfer_amount[1]) " 0) { perror("Thread #1 creation failed."); exit (1); } if (threadid[2] = pthread_create(&pthread[2], NULL, transferBA, (void*)&transfer_amount[2]) < 0) { perror("Thread #2 creation failed."); exit (1); } if (threadid[3] = pthread_create(&pthread[3], NULL, transferBA, (void*)&transfer_amount[3]) < 0) { perror("Thread #3 creation failed."); exit (1); } printf("Transitions are in progress.."); while ((accountA.balance != final_balanceA) && (accountB.balance != final_balanceB)) { printf(".."); } printf("\nAll the money is transferred !!\n"); }

            使用 gcc 來編譯這個程序,如下所示:

            # gcc -g -o gdbtest2 gdbtest2.c -L/lib/tls -lpthread
            

            程序 gdbtest2 會掛起,不會返回一條 All the money is transferred !! 消息。

            將 gdb 附加到正在運行的進程上,從而了解這個進程內(nèi)部正在發(fā)生什么。


            清單 21. 將 gdb 附加到正在運行的進程上
                            
            # ps -ef |grep gdbtest2
            root      9510  8065  1 06:30 pts/1    00:00:00 ./gdbtest2
            root      9516  9400  0 06:30 pts/4    00:00:00 grep gdbtest2
            # gdb -pid 9510
            GNU gdb 6.2.1
            Copyright 2004 Free Software Foundation, Inc.
            GDB is free software, covered by the GNU General Public License, and you 
            are welcome to change it and/or distribute copies of it under certain 
            conditions.
            Type "show copying" to see the conditions.
            There is absolutely no warranty for GDB.  Type "show warranty" for 
            details.
            This GDB was configured as "ppc-suse-linux".
            Attaching to process 9510
            Reading symbols from /root/test/gdbtest2...done.
            Using host libthread_db library "/lib/tls/libthread_db.so.1".
            Reading symbols from /lib/tls/libpthread.so.0...done.
            [Thread debugging using libthread_db enabled]
            [New Thread 1073991712 (LWP 9510)]
            [New Thread 1090771744 (LWP 9514)]
            [New Thread 1086577440 (LWP 9513)]
            [New Thread 1082383136 (LWP 9512)]
            [New Thread 1078188832 (LWP 9511)]
            Loaded symbols for /lib/tls/libpthread.so.0
            Reading symbols from /lib/tls/libc.so.6...done.
            Loaded symbols for /lib/tls/libc.so.6
            Reading symbols from /lib/ld.so.1...done.
            Loaded symbols for /lib/ld.so.1
            0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
            (gdb) info thread
              5 Thread 1078188832 (LWP 9511)  0x0ffe94ec in __lll_lock_wait () 
               from /lib/tls/libpthread.so.0
              4 Thread 1082383136 (LWP 9512)  0x0ffe94ec in __lll_lock_wait () 
               from /lib/tls/libpthread.so.0
              3 Thread 1086577440 (LWP 9513)  0x0ffe94ec in __lll_lock_wait () 
               from /lib/tls/libpthread.so.0
              2 Thread 1090771744 (LWP 9514)  0x0ffe94ec in __lll_lock_wait () 
                    from /lib/tls/libpthread.so.0
              1 Thread 1073991712 (LWP 9510)  0x0ff4ac40 in __write_nocancel () 
                 from /lib/tls/libc.so.6
            (gdb)
            

            info thread 命令中,我們可以了解到除了主線程(thread #1)之外的所有線程都在等待函數(shù) __lll_lock_wait () 完成。

            使用 thread apply threadno where 命令來查看每個線程到底運行到了什么地方:


            清單 22. 查看每個線程運行到了什么地方
                            
            (gdb) thread apply 1 where
            Thread 1 (Thread 1073991712 (LWP 9510)):
            #0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
            #1  0x0ff4ac28 in __write_nocancel () from /lib/tls/libc.so.6
            Previous frame identical to this frame (corrupt stack?)
            #0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
            (gdb) thread apply 2 where
            Thread 2 (Thread 1090771744 (LWP 9514)):
            #0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
            #1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            Previous frame inner to this frame (corrupt stack?)
            #0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
            (gdb) thread apply 3 where
            Thread 3 (Thread 1086577440 (LWP 9513)):
            #0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
            #1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            Previous frame inner to this frame (corrupt stack?)
            #0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
            (gdb) thread apply 4 where
            Thread 4 (Thread 1082383136 (LWP 9512)):
            #0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
            #1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            Previous frame inner to this frame (corrupt stack?)
            #0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
             (gdb) thread apply 5 where
            Thread 5 (Thread 1078188832 (LWP 9511)):
            #0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
            #1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            #4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
            Previous frame inner to this frame (corrupt stack?)
            #0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
            

            每個線程都試圖對一個互斥體進行加鎖,但是這個互斥體卻是不可用的,可能是因為有另外一個線程已經(jīng)對其進行加鎖了。從上面的證據(jù)我們可以判斷程序中一定存在死鎖。您還可以看到哪個線程現(xiàn)在擁有這個互斥體。


            清單 23. 查看哪個線程擁有互斥體
                            
            (gdb) print AccountA_mutex
            $1 = {__m_reserved = 2, __m_count = 0, __m_owner = 0x2527, 
            __m_kind = 0, __m_lock = {__status = 1, __spinlock = 0}} (gdb) print 0x2527 $2 = 9511 (gdb) print AccountB_mutex $3 = {__m_reserved = 2, __m_count = 0, __m_owner = 0x2529,
            __m_kind = 0, __m_lock = {__status = 1, __spinlock = 0}} (gdb) print 0x2529 $4 = 9513 (gdb)

            從上面的命令中,我們可以看出 AccontA_mutex 是被線程 5(LWP 9511)加鎖(擁有)的,而 AccontB_mutex 是被線程 3(LWP 9513)加鎖(擁有)的。

            為了解決上面的死鎖情況,可以按照相同的順序?qū)コ怏w進行加鎖,如下所示:


            清單 24. 按照相同的順序?qū)コ怏w進行加鎖
                                
            .
            .
            void * transferAB (void* amount_ptr) {
                    int amount = *((int*)amount_ptr);
                    pthread_mutex_lock(&AccountA_mutex);
                    pthread_mutex_lock(&AccountB_mutex);
                    if (accountA.balance < amount)   {
                            printf("There is not enough memory in Account A!\n");
                            pthread_mutex_unlock(&AccountA_mutex);
                            pthread_exit((void *)1);
                    }
                    accountA.balance -=amount;
                    sleep(1);
                    accountB.balance +=amount;
                    pthread_mutex_unlock(&AccountA_mutex);
                    pthread_mutex_unlock(&AccountB_mutex);
            }
            void * transferBA (void* amount_ptr) {
                    int amount = *((int*)amount_ptr);
                    pthread_mutex_lock(&AccountA_mutex);
                    pthread_mutex_lock(&AccountB_mutex);
                    if (accountB.balance < amount)   {
                            printf("There is not enough memory in Account B!\n");
                            pthread_mutex_unlock(&AccountB_mutex);
                            pthread_exit((void *)1);
                    }
                    accountB.balance -=amount;
                    sleep(1);
                    accountA.balance +=amount;
                    pthread_mutex_unlock(&AccountA_mutex);
                    pthread_mutex_unlock(&AccountB_mutex);
            }
            .
            .
            

            或者對每個帳號單獨進行加鎖,如下所示:


            清單 25. 對每個帳號單獨進行加鎖
                                
            .
            .
            void * transferAB (void* amount_ptr) {
                    int amount = *((int*)amount_ptr);
                    pthread_mutex_lock(&AccountA_mutex);
                    if (accountA.balance < amount)   {
                            printf("There is not enough memory in Account A!\n");
                            pthread_mutex_unlock(&AccountA_mutex);
                            pthread_exit((void *)1);
                    }
                    accountA.balance -=amount;
                    sleep(1);
                    pthread_mutex_unlock(&AccountA_mutex);
                    pthread_mutex_lock(&AccountB_mutex); 
                    accountB.balance +=amount;
                    pthread_mutex_unlock(&AccountB_mutex);
            }
            void * transferBA (void* amount_ptr) {
                    int amount = *((int*)amount_ptr);
                    pthread_mutex_lock(&AccountB_mutex);
                    if (accountB.balance < amount)   {
                            printf("There is not enough memory in Account B!\n");
                            pthread_mutex_unlock(&AccountB_mutex);
                            pthread_exit((void *)1);
                    }
                    accountB.balance -=amount;
                    sleep(1);
                    pthread_mutex_unlock(&AccountB_mutex);
                    pthread_mutex_lock(&AccountA_mutex); 
                    accountA.balance +=amount;
                    pthread_mutex_unlock(&AccountA_mutex);
            }
            .
            .
            .
            

            要調(diào)試 64 位的應(yīng)用程序(使用 GCC 的 –m64 選項或 IBM XL C/C++ 編譯器的 –q64 選項編譯),應(yīng)該使用一個特別的 gdb 版本 gdb64。

            Java 調(diào)試器

            Java? 調(diào)試器 JDB 是一個用來調(diào)試 Java 類的命令行調(diào)試器。它提供了對本地或遠程 Java 虛擬機(JVM)的檢查和調(diào)試功能。其二進制文件是 jdb。

            JDB 是與 java 編譯器 javac 一起打包的,在 java2 rpm 包中。

            有很多方法都可以開始一個 jdb 會話。最常見的方法是讓 jdb 啟動一個新的 Java 虛擬機,其中運行要調(diào)試的應(yīng)用程序的主類。這可以通過在命令行中使用命令 jdb 替換 java 來實現(xiàn)。例如,如果您的應(yīng)用程序的主類是 appClass,那么就使用下面的命令在 JDB 中調(diào)試這個程序:

            # jdb appClass
            

            另外一種使用 jdb 的方法是將其附加到一個早已在運行的 JVM 上。要使用 jdb 進行調(diào)試的 VM 必須是使用下面的選項啟動的:

            # java -Xdebug -Xnoagent - 
            Xrunjdwp:transport=dt_socket,server=y,suspend=n,
            address=8888 -Djava.compiler=NONEappClass

            然后您就可以使用下面的命令將 jdb 附加到這個 VM 上:

            # jdb -attach 8888

            jdb 最常用的命令與 gdb 的類似。詳細信息請參考 表 1

            圖形化調(diào)試器

            使用圖形模式的調(diào)試器相對于命令行調(diào)試器的一個優(yōu)點是,在調(diào)試器中單步執(zhí)行程序的同時可以看到對應(yīng)的每行源代碼。

            GNU DDD(Data Display Debugger)就是一個調(diào)試器(例如 GDB 和 JDB)的圖形化前端。除了常見的前端特性(例如查看源代碼)之外,DDD 還通過將要顯示的數(shù)據(jù)結(jié)構(gòu)以交互式的圖形化方式進行顯示而聞名。

            對于 SLES 9 來說,用于 PowerPC 的 DDD 二進制文件是在 SUSE Linux SDK CD 中單獨提供的,也可以從 Novell 公司的網(wǎng)站上進行下載(參閱 參考資料)。RedHat 在 RHEL AS 4 CD 中提供了 DDD 的 rpm 包。

            圖 2 是在使用 DDD 來調(diào)試 清單 19 (gdbtest3.cpp)中的例子時的截圖。


            圖 2. DDD 截屏

            默認情況下,DDD 使用 gdb 作為后端調(diào)試器;要切換到 jdb,請使用 ddd -jdb 來啟動 DDD。

            有關(guān) DDD 的更多信息,請參考 GNU 項目 Web 站點上的 DDD 部分(參閱 參考資料)。

            strace

            strace 命令是可以在 Linux on POWER 架構(gòu)上使用的一個功能非常強大的工具。它可以顯示用戶空間的應(yīng)用程序所執(zhí)行的全部系統(tǒng)調(diào)用。strace 可以以符號表的形式顯示這些調(diào)用的參數(shù)和返回值。strace 從內(nèi)核接收信息,并不需要采用任何特殊的方式來構(gòu)建內(nèi)核。要跟蹤的應(yīng)用程序也不需要為 strace 重新進行編譯,當我們無法訪問應(yīng)用程序的源代碼時,這是非常方便的一種方法。

            下面的例子使用 strace 來跟蹤一個普通用戶執(zhí)行 cat /etc/shadow 的過程,然后將跟蹤到的結(jié)果打印到 strace.cat.out 中。這個程序依然正常運行,但是在使用 strace 運行時速度稍微有些慢;最后,我們得到一個跟蹤文件。

            $ strace -o strace.cat.out cat /etc/shadow
            cat: /etc/shadow: Permission denied
            $

            跟蹤文件通常都很大。即使對這個簡單的例子來說,strace.cat.out 也有 111 行長。這個文件的最后包含下面的行:


            清單 26. strace.cat.out 文件中的部分行
                            
                 88 open("/usr/lib/locale/en_US.UTF-8/LC_NUMERIC", 
                      O_RDONLY) = -1 ENOENT (No such file or directory)
                 89 open("/usr/lib/locale/en_US.utf8/LC_NUMERIC", O_RDONLY) = 3
                 90 fstat64(3, {st_mode=S_IFREG|0644, st_size=54, ...}) = 0
                 91 mmap(NULL, 54, PROT_READ, MAP_PRIVATE, 3, 0) = 0x4010f000
                 92 close(3)                                = 0
                 93 open("/usr/lib/locale/en_US.UTF-8/LC_CTYPE", 
                        O_RDONLY) = -1 ENOENT (No such file or directory)
                 94 open("/usr/lib/locale/en_US.utf8/LC_CTYPE", O_RDONLY) = 3
                 95 fstat64(3, {st_mode=S_IFREG|0644, st_size=208464, ...}) = 0
                 96 mmap(NULL, 208464, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40110000
                 97 close(3)                                = 0
                 98 fstat64(1, {st_mode=S_IFCHR|0620, 
                      st_rdev=makedev(136, 4), ...}) = 0
                 99 open("/etc/shadow", O_RDONLY|O_LARGEFILE) = -1 EACCES 
                       (Permission denied)
                100 write(2, "cat: ", 5)                    = 5
                101 write(2, "/etc/shadow", 11)             = 11
                102 open("/usr/share/locale/en_US.UTF-8/LC_MESSAGES/libc.mo", 
                       O_RDONLY) = -1 ENOENT (No such file or directory)
                103 open("/usr/share/locale/en_US.utf8/LC_MESSAGES/libc.mo", 
                        O_RDONLY) = -1 ENOENT (No such file or directory)
                104 open("/usr/share/locale/en_US/LC_MESSAGES/libc.mo", 
                       O_RDONLY) = -1 ENOENT (No such file or directory)
                105 open("/usr/share/locale/en.UTF-8/LC_MESSAGES/libc.mo", 
                       O_RDONLY) = -1 ENOENT (No such file or directory)
                106 open("/usr/share/locale/en.utf8/LC_MESSAGES/libc.mo", 
                      O_RDONLY) = -1 ENOENT (No such file or directory)
                107 open("/usr/share/locale/en/LC_MESSAGES/libc.mo", 
                      O_RDONLY) = -1 ENOENT (No such file or directory)
                108 write(2, ": Permission denied", 19)     = 19
                109 write(2, "\n", 1)                       = 1
                110 close(1)                                = 0
                111 exit_group(1)                 
            

            注意在第 99 行處,命令會失效,因為系統(tǒng)調(diào)用 open("/etc/shadow", O_RDONLY|O_LARGEFILE) 失效了,返回了一個 EACCESS 錯誤代碼,這說明權(quán)限不符合。

            在有些情況中,應(yīng)用程序可能會掛起,并且不能響應(yīng)諸如 ctrl+c(SIGINT)之類的信號。這說明應(yīng)用程序正在調(diào)用的系統(tǒng)調(diào)用在其內(nèi)核模式下掛起了,一直不會返回用戶模式。strace 可以非常有用地用來判斷是哪一個系統(tǒng)調(diào)用,以及傳給這個系統(tǒng)調(diào)用的參數(shù)是什么。可能性最大的情況是參數(shù)的位置不對而導(dǎo)致了問題。

            與 gdb64 是用于 64 位應(yīng)用程序的 gdb 類似,strace64 也用來跟蹤 64 位應(yīng)用程序所請求的系統(tǒng)調(diào)用。

            posted @ 2011-08-05 09:29 Roger 閱讀(335) | 評論 (0)編輯 收藏

            select 和pselect的區(qū)別

            /* According to POSIX 1003.1-2001 */ 
            #include <sys/select.h>

            /* According to earlier standards */ 
            #include <sys/time.h>
             
            #include <sys/types.h>
             
            #include <unistd.h>

            int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

            int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);

            FD_CLR(int fd, fd_set *set); 
            FD_ISSET(int fd, fd_set *set); 
            FD_SET(int fd, fd_set *set); 
            FD_ZERO(fd_set *set); 
             

            以上是關(guān)于selectpselect技術(shù)的函數(shù)族。

            下面對selectpselect函數(shù)原型的說明:

            Selectpselect都是等待一系列的文件描述符(int)的狀態(tài)發(fā)生變化。

            這兩個函數(shù)基本上是一致,但是有三個區(qū)別:

            第一點    select函數(shù)用的timeout參數(shù),是一個timeval的結(jié)構(gòu)體(包含秒和微秒),然而pselect用的是一個timespec結(jié)構(gòu)體(包含秒和納秒)

            第二點    select函數(shù)可能會為了指示還剩多長時間而更新timeout參數(shù),然而pselect不會改變timeout參數(shù)

            第三點    select函數(shù)沒有sigmask參數(shù),當pselectsigmask參數(shù)為null時,兩者行為時一致的。

            三個獨立的文件描述符集合將會被監(jiān)視,在readset中的文件描述符將會被監(jiān)視是不是可讀,就是說讀這個文件描述符是否會發(fā)生阻塞。同理writeset集合將會被監(jiān)視寫過程是不是會被阻塞,exceptfds 將會被監(jiān)視是否會發(fā)生異常。函數(shù)退出的時候,這些文件描述符集合將會發(fā)生改變用于標示文件描述符的狀態(tài)變化。

            有四個宏函數(shù)是用于提供多工集合監(jiān)視的:FD_ZERO用以清空一個集合,FD_SETFD_CLR是用來從一個集合中添加和刪除一個文件描述符,FD_ISSET是用來測試一個文件描述符是不是在一個集合中。

             

             

            EXAMPLE

            #include <stdio.h>

            #include <sys/time.h>

            #include <sys/types.h>

            #include <unistd.h>

             

            int

            main(void) {

                fd_set rfds;

                struct timeval tv;

                int retval;

             

                /* Watch stdin (fd 0) to see when it has input. */

                FD_ZERO(&rfds);

                FD_SET(0, &rfds);

                /* Wait up to five seconds. */

                tv.tv_sec = 5;

                tv.tv_usec = 0;

             

                retval = select(1, &rfds, NULL, NULL, &tv);

                /* Don't rely on the value of tv now! */

             

                if (retval == -1)

                    perror("select()");

                else if (retval)

                    printf("Data is available now.\n");

                    /* FD_ISSET(0, &rfds) will be true. */

                else

                    printf("No data within five seconds.\n");

             

                return 0;

            }

             

            posted @ 2011-06-27 23:04 Roger 閱讀(6636) | 評論 (1)編輯 收藏

            給vs2005安裝boost正則庫

            1.       下載boost庫,并解壓(此處假設(shè)boost庫的版本為boost_1_34_0,并解壓至E:\boost_1_34_0

            2.       編譯動態(tài)庫,這里只安裝正則庫,因此只要生成boost庫中關(guān)于regex模塊的動態(tài)庫即可。
            具體操作如下:
            打開命令提示符
            輸入命令“cd E:\boost_1_34_0\libs\regex\build”,設(shè)置當前目錄
            3設(shè)置VC2005的配置環(huán)境變量,找到Microsoft Visual Studio 8.0\VC\bin\VCVARS32.BAT文件,把VCVARS32.BAT文件用鼠標拖到打開的cmd窗口("F:\Program Files\Microsoft Visual Studio\VC\bin\VCVARS32.BAT"),然后回車。
            輸入命令“nmake f vc8.mak
            5等待幾分鐘,E:\boost_1_34_0\libs\regex\build多了一個vc80的文件夾,里面是很多libdll文件。

            3.       配置vs2005,“VC++目錄”,使它編譯使時候能找到正則庫。
            打開vc2005,選擇“工具->選項->項目與解決方案->顯示以下內(nèi)容的目錄-> 包含文件”,加入一行“E:\boost_1_34_0
                
            選擇“工具->選項->項目與解決方案->顯示以下內(nèi)容的目錄-> 庫文件”,加入一行“E:\boost_1_34_0 \libs\regex\build\vc80

            posted @ 2011-05-27 21:53 Roger 閱讀(425) | 評論 (0)編輯 收藏

            僅列出標題
            共2頁: 1 2 
            国产精品久久久久久| 国产精品久久久久蜜芽| 亚洲综合精品香蕉久久网97 | 亚洲国产成人久久精品影视| 国产成人精品久久综合| 久久精品国产免费观看| 99久久精品无码一区二区毛片| 国产精品日韩欧美久久综合| 亚洲成色www久久网站夜月| 日本一区精品久久久久影院| 亚洲午夜久久久久久久久电影网 | 久久se精品一区精品二区国产| 亚洲国产精品无码久久久不卡| 国产高潮久久免费观看| 综合久久国产九一剧情麻豆| 久久久久国产精品麻豆AR影院| 91精品国产高清久久久久久io| 亚洲精品第一综合99久久| 伊人久久大香线蕉精品| 久久久久人妻精品一区二区三区| 日韩美女18网站久久精品| 91精品国产91久久久久久青草| 亚洲国产精品久久久天堂| 久久影院午夜理论片无码| 久久国产精品-久久精品| 亚洲国产精品18久久久久久| 波多野结衣久久一区二区| 久久综合日本熟妇| 久久久久久亚洲精品无码| 精品99久久aaa一级毛片| 香港aa三级久久三级| 久久青青草原综合伊人| 久久久久久国产精品无码超碰| 亚洲综合日韩久久成人AV| 久久久久久国产精品美女| 久久久无码精品亚洲日韩蜜臀浪潮| 欧美久久天天综合香蕉伊| 青青热久久国产久精品| 久久青青草原精品国产软件| 久久伊人影视| 久久精品国产亚洲AV蜜臀色欲 |