偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

搞懂 Core Dump,調(diào)試崩潰不用愁

系統(tǒng) Linux
Core Dump 就是程序崩潰時(shí),操作系統(tǒng)幫你 “凍結(jié)” 的一份 “現(xiàn)場(chǎng)快照”,里面記錄了程序崩潰瞬間的內(nèi)存數(shù)據(jù)、函數(shù)調(diào)用棧、寄存器狀態(tài)等關(guān)鍵信息。只要掌握了它,你不用再靠 “猜” 和 “試”排查問(wèn)題,就能精準(zhǔn)定位到代碼里的 bug。

你是否也曾遇到過(guò)這樣的場(chǎng)景:寫(xiě)好的程序在本地跑得好好的,一到線(xiàn)上就突然崩潰,日志里只留下一句模糊的 “Segmentation fault”;或者調(diào)試時(shí)遇到內(nèi)存越界、空指針問(wèn)題,反復(fù)排查代碼卻始終找不到癥結(jié),只能對(duì)著屏幕發(fā)呆?這些讓人頭疼的崩潰問(wèn)題,其實(shí)都有一個(gè) “破局利器”——Core Dump。

簡(jiǎn)單來(lái)說(shuō),Core Dump 就是程序崩潰時(shí),操作系統(tǒng)幫你 “凍結(jié)” 的一份 “現(xiàn)場(chǎng)快照”,里面記錄了程序崩潰瞬間的內(nèi)存數(shù)據(jù)、函數(shù)調(diào)用棧、寄存器狀態(tài)等關(guān)鍵信息。只要掌握了它,你不用再靠 “猜” 和 “試”排查問(wèn)題,就能精準(zhǔn)定位到代碼里的 bug。接下來(lái)內(nèi)容不會(huì)講復(fù)雜的底層原理,而是聚焦實(shí)用技能:從怎么配置才能讓程序生成 Core Dump 文件,到用 GDB 快速分析快照找到崩潰原因,再到避開(kāi)那些導(dǎo)致 Core Dump 生成失敗的坑。無(wú)論你是剛接觸 Linux 開(kāi)發(fā)的新手,還是常跟線(xiàn)上疑難雜癥打交道的資深工程師,這套方法都能讓你在遇到程序突然“暴斃”時(shí),做到心中有數(shù)、手中有術(shù)。

一、什么是 Core Dump?

1.1coredump文件介紹

在 Linux 系統(tǒng)下進(jìn)行程序開(kāi)發(fā)時(shí),你是否遇到過(guò)程序突然崩潰,然后終端上出現(xiàn) “Segmentation fault (core dumped)” 這樣的提示?這其實(shí)就是程序發(fā)生了 Core Dump。簡(jiǎn)單來(lái)說(shuō),Core Dump(核心轉(zhuǎn)儲(chǔ))是操作系統(tǒng)在進(jìn)程收到某些信號(hào)而異常終止時(shí),將進(jìn)程地址空間的內(nèi)容以及有關(guān)進(jìn)程狀態(tài)的其他信息寫(xiě)出的一個(gè)磁盤(pán)文件 。這個(gè)文件就像是程序崩潰瞬間的 “現(xiàn)場(chǎng)快照”,對(duì)我們調(diào)試程序、定位問(wèn)題有著至關(guān)重要的作用。

想象一下,你精心編寫(xiě)了一個(gè)復(fù)雜的程序,在測(cè)試過(guò)程中,它卻突然毫無(wú)征兆地崩潰了。如果沒(méi)有 Core Dump,你可能只能對(duì)著代碼干瞪眼,很難確定問(wèn)題究竟出在哪里。但有了 Core Dump 文件,我們就可以借助調(diào)試工具(如 GDB),深入分析程序崩潰時(shí)的內(nèi)存狀態(tài)、寄存器值、函數(shù)調(diào)用棧等信息,從而找出程序崩潰的真正原因 。比如說(shuō),是因?yàn)樵L問(wèn)了空指針,還是數(shù)組越界,亦或是其他隱藏在代碼深處的 Bug。

coredump文件格式如圖所示:

圖片圖片

coredump文件格式包含4個(gè)部分:ELF頭、程序頭表、NOTE段和LOAD段。

1.2從內(nèi)核看coredump文件生成過(guò)程

coredump文件生成過(guò)程包含以下幾個(gè)步驟:

  • 步驟1:程序觸發(fā)致命信號(hào),程序執(zhí)行非法操作(如段錯(cuò)誤、總線(xiàn)錯(cuò)誤、除零或主動(dòng)中止)時(shí),會(huì)觸發(fā)CPU硬件異常并由操作系統(tǒng)內(nèi)核向進(jìn)程發(fā)送相應(yīng)信號(hào)。
  • 步驟2:內(nèi)核處理信號(hào)并終止進(jìn)程,內(nèi)核在向目標(biāo)進(jìn)程遞送信號(hào)后會(huì)立即凍結(jié)該進(jìn)程以保持狀態(tài)穩(wěn)定,隨后按照默認(rèn)處理方式終止該進(jìn)程并生成核心轉(zhuǎn)儲(chǔ)文件。
  • 步驟3:寫(xiě)入Coredump文件,內(nèi)核會(huì)在進(jìn)程工作目錄創(chuàng)建核心轉(zhuǎn)儲(chǔ)文件(通常命名為core或core.<pid>),完整記錄崩潰時(shí)的進(jìn)程虛擬地址空間、CPU寄存器狀態(tài)、線(xiàn)程信息、信號(hào)詳情及相關(guān)元數(shù)據(jù)。
  • 步驟4:資源清理與進(jìn)程終止,在Coredump文件成功寫(xiě)入后,內(nèi)核會(huì)回收該進(jìn)程占用的所有剩余資源(包括內(nèi)存、文件描述符、信號(hào)量等),并最終將該進(jìn)程完全從系統(tǒng)中移除。

我們可以通過(guò)下面這張圖來(lái)直觀理解coredump文件的生成過(guò)程:

圖片圖片

(1)信號(hào)處理階段:do_signal 函數(shù)

當(dāng)進(jìn)程從內(nèi)核態(tài)返回用戶(hù)態(tài)之前,內(nèi)核會(huì)仔細(xì)檢查進(jìn)程的信號(hào)隊(duì)列,查看是否存在未處理的信號(hào)。這就好比一個(gè)快遞員在送完所有快遞后,會(huì)檢查自己的包裹清單,看看是否有遺漏的快遞。如果發(fā)現(xiàn)有未處理的信號(hào),內(nèi)核就會(huì)調(diào)用do_signal函數(shù)來(lái)處理這些信號(hào)。

do_signal函數(shù)在整個(gè) coredump 文件生成過(guò)程中扮演著關(guān)鍵的角色,它主要調(diào)用get_signal_to_deliver函數(shù)來(lái)獲取需要處理的信號(hào),并根據(jù)信號(hào)的類(lèi)型和相關(guān)設(shè)置進(jìn)行后續(xù)處理。下面是一段簡(jiǎn)化的do_signal函數(shù)代碼示例(基于 Linux 內(nèi)核源碼,實(shí)際代碼更為復(fù)雜,這里僅為展示關(guān)鍵邏輯):

static void fastcall do_signal(struct pt_regs *regs) {
    siginfo_t info;
    int signr;
    struct k_sigaction ka;
    sigset_t *oldset;

    // 調(diào)用get_signal_to_deliver函數(shù)獲取信號(hào)
    signr = get_signal_to_deliver(&info, &ka, regs, NULL); 
    if (signr > 0) {
        // 處理信號(hào)的邏輯
        // 這里可能會(huì)根據(jù)信號(hào)類(lèi)型進(jìn)行不同操作,比如生成coredump文件等
    }
}

在這段代碼中,get_signal_to_deliver函數(shù)的返回值signr表示獲取到的信號(hào)。如果signr大于 0,說(shuō)明獲取到了有效的信號(hào),接下來(lái)就會(huì)進(jìn)入處理信號(hào)的邏輯。在實(shí)際的內(nèi)核代碼中,這個(gè)處理過(guò)程涉及到復(fù)雜的信號(hào)處理機(jī)制,包括信號(hào)的屏蔽、恢復(fù),以及根據(jù)信號(hào)類(lèi)型執(zhí)行相應(yīng)的操作。

(2)獲取信號(hào)階段:get_signal_to_deliver 函數(shù)

get_signal_to_deliver函數(shù)的主要任務(wù)是從進(jìn)程的信號(hào)隊(duì)列中獲取一個(gè)信號(hào),并根據(jù)信號(hào)的類(lèi)型進(jìn)行不同的操作。它就像是從一個(gè)裝滿(mǎn)各種信號(hào) “包裹” 的倉(cāng)庫(kù)中,挑選出需要處理的 “包裹”。

這個(gè)函數(shù)會(huì)遍歷進(jìn)程的信號(hào)隊(duì)列,檢查每個(gè)信號(hào)的狀態(tài)和屬性。對(duì)于一些特殊的信號(hào),比如 SIGKILL(用于強(qiáng)制終止進(jìn)程),會(huì)直接在內(nèi)核態(tài)進(jìn)行處理,不會(huì)生成 coredump 文件。而對(duì)于那些會(huì)導(dǎo)致進(jìn)程異常退出并生成 coredump 文件的信號(hào),如 SIGSEGV(段錯(cuò)誤信號(hào))、SIGABRT(異常終止信號(hào))等,get_signal_to_deliver函數(shù)會(huì)進(jìn)行相應(yīng)的處理,為生成 coredump 文件做準(zhǔn)備。

下面是一段get_signal_to_deliver函數(shù)的關(guān)鍵代碼分析(同樣是簡(jiǎn)化后的代碼,用于展示核心邏輯):

int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
                          struct pt_regs *regs, void *cookie) {
    sigset_t *mask = ¤t->blocked;
    int signr = 0;

    // 循環(huán)從信號(hào)隊(duì)列中獲取信號(hào)
    while ((signr = dequeue_signal(mask, ¤t->pending)) ||
           (signr = dequeue_signal(mask, ¤t->shared_pending))) {
        struct sigpending *pending;
        struct sigqueue *q;

        // 獲取信號(hào)對(duì)應(yīng)的sigqueue
        q = find_signal_queue(signr, ¤t->pending);
        if (!q)
            q = find_signal_queue(signr, ¤t->shared_pending);

        // 填充信號(hào)信息
        *info = q->info;
        *return_ka = current->sigaction[signr - 1];

        // 處理與coredump相關(guān)的信號(hào)邏輯
        if (should_generate_coredump(signr)) {
            // 進(jìn)行生成coredump文件的前期準(zhǔn)備工作
            prepare_coredump();
        }

        // 其他信號(hào)處理邏輯

        return signr;
    }
    return 0;
}

在這段代碼中,通過(guò)dequeue_signal函數(shù)從進(jìn)程的私有信號(hào)隊(duì)列current->pending和共享信號(hào)隊(duì)列current->shared_pending中獲取信號(hào)。如果獲取到信號(hào),就會(huì)找到對(duì)應(yīng)的sigqueue,填充信號(hào)信息到info和return_ka中。對(duì)于那些需要生成 coredump 文件的信號(hào)(通過(guò)should_generate_coredump函數(shù)判斷),會(huì)調(diào)用prepare_coredump函數(shù)進(jìn)行前期準(zhǔn)備工作,例如初始化一些與 coredump 生成相關(guān)的數(shù)據(jù)結(jié)構(gòu)、檢查系統(tǒng)配置等 。

(3)內(nèi)存信息記錄階段

當(dāng)內(nèi)核確定要生成 coredump 文件后,就會(huì)開(kāi)始根據(jù)進(jìn)程當(dāng)時(shí)的內(nèi)存信息來(lái)生成這個(gè)文件。這一步就像是給進(jìn)程的內(nèi)存狀態(tài)拍一張 “照片”,然后把這張 “照片” 保存到 coredump 文件中。

coredump 文件本質(zhì)上是一個(gè) ELF 格式的文件,它主要包含兩種類(lèi)型的 segment:PT_NOTE 和 PT_LOAD。

PT_NOTE 類(lèi)型的 segment 記錄了解析 memory 區(qū)域的關(guān)鍵信息。它被分成了多個(gè)elf_note結(jié)構(gòu),其中NT_PRSTATUS類(lèi)型記錄了復(fù)位前 CPU 的寄存器信息,這些信息對(duì)于分析程序崩潰時(shí)的 CPU 狀態(tài)非常重要,就像是記錄了相機(jī)拍攝瞬間的各種參數(shù)設(shè)置;NT_TASKSTRUCT記錄了進(jìn)程的task_struct信息,包含了進(jìn)程的各種屬性和狀態(tài);還有一個(gè)自定義的VMCOREINFO結(jié)構(gòu)記錄了內(nèi)核的一些關(guān)鍵信息,比如內(nèi)核版本、編譯選項(xiàng)等,這些信息為分析提供了更多的上下文。

PT_LOAD 類(lèi)型的 segment 則用于記錄進(jìn)程的內(nèi)存內(nèi)容,每個(gè) segment 對(duì)應(yīng)一段內(nèi)存區(qū)域,記錄了這段內(nèi)存對(duì)應(yīng)的物理地址、虛擬地址、長(zhǎng)度以及訪問(wèn)權(quán)限(讀、寫(xiě)、執(zhí)行等)。這些信息是通過(guò)遍歷進(jìn)程中的每個(gè)虛擬內(nèi)存區(qū)域(VMA,Virtual Memory Area)來(lái)設(shè)置的,然后將 VMA 的內(nèi)容寫(xiě)入到 coredump 文件中。例如,進(jìn)程的堆、棧、數(shù)據(jù)段等都會(huì)在 PT_LOAD 類(lèi)型的 segment 中有所體現(xiàn),它們就像是照片中的各種物體,展示了程序運(yùn)行時(shí)的內(nèi)存布局情況。

通過(guò)這一系列的步驟,內(nèi)核就完成了 coredump 文件的生成,為后續(xù)的程序調(diào)試和問(wèn)題分析提供了重要的依據(jù)。

二、Core Dump 的生成機(jī)制

2.1觸發(fā)條件

Core Dump 的生成通常是由一些特定的信號(hào)觸發(fā)的。當(dāng)程序接收到這些信號(hào)且沒(méi)有對(duì)其進(jìn)行特殊處理時(shí),就可能會(huì)產(chǎn)生 Core Dump。常見(jiàn)的能觸發(fā) Core Dump 的信號(hào)有以下幾種:

①SIGSEGV(信號(hào) 11):這個(gè)信號(hào)表示段錯(cuò)誤(Segmentation Fault),通常是由于程序進(jìn)行了非法的內(nèi)存訪問(wèn)操作。比如訪問(wèn)空指針、數(shù)組越界或者使用已經(jīng)釋放的內(nèi)存。以 C 語(yǔ)言代碼為例:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = NULL;
    *ptr = 10; // 訪問(wèn)空指針,會(huì)觸發(fā)SIGSEGV信號(hào)
    return 0;
}

在這段代碼中,我們定義了一個(gè)空指針ptr,然后嘗試向它所指向的位置寫(xiě)入數(shù)據(jù),這是非法的內(nèi)存訪問(wèn)操作,會(huì)導(dǎo)致程序接收到 SIGSEGV 信號(hào),進(jìn)而可能產(chǎn)生 Core Dump。

②SIGABRT(信號(hào) 6):此信號(hào)一般由程序調(diào)用abort函數(shù)引發(fā),或者在斷言(assert)失敗時(shí)產(chǎn)生。例如:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main() {
    int num = 0;
    assert(num > 0); // 斷言失敗,會(huì)觸發(fā)SIGABRT信號(hào)
    return 0;
}

這里我們使用assert來(lái)檢查num是否大于 0,由于num的值為 0,斷言失敗,程序會(huì)觸發(fā) SIGABRT 信號(hào),可能生成 Core Dump 文件。

③SIGFPE(信號(hào) 8):該信號(hào)在發(fā)生致命的算術(shù)運(yùn)算錯(cuò)誤時(shí)發(fā)出,比如除零操作??聪旅娴拇a:

#include <stdio.h>

int main() {
    int a = 10;
    int b = 0;
    int c = a / b; // 除零操作,會(huì)觸發(fā)SIGFPE信號(hào)
    return 0;
}

這段代碼中進(jìn)行了除零運(yùn)算,這是一種致命的算術(shù)錯(cuò)誤,會(huì)導(dǎo)致程序接收到 SIGFPE 信號(hào),有很大概率產(chǎn)生 Core Dump。

2.2配置要點(diǎn)

在 Linux 系統(tǒng)中,要讓程序在崩潰時(shí)生成 Core Dump 文件,需要進(jìn)行一些配置。

  1. ulimit -c 命令:默認(rèn)情況下,系統(tǒng)對(duì) Core 文件的大小限制可能為 0,這意味著程序崩潰時(shí)不會(huì)生成 Core 文件。我們可以使用ulimit -c命令來(lái)設(shè)置 Core 文件大小的上限。如果想要生成不受大小限制的 Core 文件,可以執(zhí)行ulimit -c unlimited命令。比如,在終端中先執(zhí)行ulimit -c unlimited,然后再運(yùn)行可能會(huì)崩潰的程序,這樣當(dāng)程序異常崩潰時(shí),就更有可能生成完整的 Core Dump 文件。
  2. 程序運(yùn)行目錄權(quán)限:程序運(yùn)行的當(dāng)前目錄必須對(duì)進(jìn)程具有寫(xiě)權(quán)限,否則無(wú)法將 Core 文件保存到該目錄。假設(shè)我們的程序在/home/user/app目錄下運(yùn)行,如果該目錄沒(méi)有寫(xiě)入權(quán)限,即使程序崩潰觸發(fā)了 Core Dump 生成條件,也無(wú)法在該目錄下生成 Core 文件。
  3. seteuid ()/setegid () 函數(shù)調(diào)用后的特殊處理:如果程序在運(yùn)行過(guò)程中調(diào)用了seteuid()或setegid()函數(shù)來(lái)改變進(jìn)程的有效用戶(hù) ID 或組 ID ,默認(rèn)情況下系統(tǒng)不會(huì)為這類(lèi)進(jìn)程生成 Core 文件。此時(shí),需要將/proc/sys/fs/suid_dumpable文件的內(nèi)容修改為 1,才能夠讓這類(lèi)進(jìn)程在崩潰時(shí)生成 Core 文件。例如,通過(guò)echo 1 > /proc/sys/fs/suid_dumpable命令來(lái)修改該文件內(nèi)容,以允許生成 Core 文件。

還可以通過(guò)修改/proc/sys/kernel/core_pattern文件來(lái)指定Core文件的生成路徑和命名規(guī)則,方便我們更好地管理生成的Core Dump文件。比如,執(zhí)行echo "/var/core/%e.%p.%h.%t" > /proc/sys/kernel/core_pattern,這樣生成的Core文件會(huì)保存在/var/core目錄下,文件名包含程序名(%e)、進(jìn)程 ID(%p)、主機(jī)名(%h)和時(shí)間戳(%t),有助于我們區(qū)分不同程序、不同進(jìn)程產(chǎn)生的Core Dump文件 。

三、引發(fā) Core Dump 的常見(jiàn)原因

3.1內(nèi)存訪問(wèn)錯(cuò)誤

(1)空指針或野指針解引用:在 C/C++ 等語(yǔ)言中,空指針是指未指向任何有效內(nèi)存地址的指針,野指針則是指向一塊已經(jīng)釋放或者從未被初始化的內(nèi)存區(qū)域的指針。當(dāng)對(duì)空指針或野指針進(jìn)行解引用操作時(shí),就相當(dāng)于在訪問(wèn)一塊無(wú)效的內(nèi)存,這會(huì)觸發(fā) SIGSEGV 信號(hào),導(dǎo)致程序崩潰并生成 Core Dump。比如在下面這段 C++ 代碼中:

#include <iostream>

int main() {
    int *ptr = nullptr;
    *ptr = 10; // 解引用空指針,會(huì)觸發(fā)SIGSEGV信號(hào)
    return 0;
}

運(yùn)行這段代碼,程序會(huì)因?yàn)樵L問(wèn)空指針而接收到 SIGSEGV 信號(hào),進(jìn)而產(chǎn)生 Core Dump。

(2)緩沖區(qū)溢出:當(dāng)我們向一個(gè)數(shù)組或緩沖區(qū)寫(xiě)入超出其大小的數(shù)據(jù)時(shí),就會(huì)發(fā)生緩沖區(qū)溢出。這不僅會(huì)覆蓋相鄰的內(nèi)存區(qū)域,破壞其他數(shù)據(jù)的完整性,還可能導(dǎo)致程序執(zhí)行到非法的內(nèi)存地址,引發(fā) SIGSEGV 信號(hào),最終造成 Core Dump。以下面的 C 語(yǔ)言代碼為例:

#include <stdio.h>

int main() {
    char buffer[10];
    // 試圖向buffer中寫(xiě)入長(zhǎng)度為15的字符串,會(huì)導(dǎo)致緩沖區(qū)溢出
    strcpy(buffer, "123456789012345"); 
    return 0;
}

這里使用strcpy函數(shù)向長(zhǎng)度為 10 的buffer數(shù)組中寫(xiě)入長(zhǎng)度為 15 的字符串,會(huì)造成緩沖區(qū)溢出,程序很可能會(huì)崩潰并生成 Core Dump。

3.2信號(hào)未正確處理

在程序運(yùn)行過(guò)程中,會(huì)收到各種各樣的信號(hào)。其中一些關(guān)鍵信號(hào),如果沒(méi)有被程序捕獲并進(jìn)行特殊處理,它們的默認(rèn)行為就是終止進(jìn)程并生成 Core Dump 。

(1)SIGSEGV:前面已經(jīng)多次提到,這個(gè)信號(hào)代表段錯(cuò)誤,主要是由于非法內(nèi)存訪問(wèn)引發(fā)的。像訪問(wèn)空指針、數(shù)組越界、使用已釋放的內(nèi)存等操作,都會(huì)讓程序收到 SIGSEGV 信號(hào),若不捕獲處理,就會(huì)導(dǎo)致 Core Dump。

(2)SIGABRT:通常由程序調(diào)用abort函數(shù),或者斷言(assert)失敗時(shí)觸發(fā)。比如在下面的代碼中:

#include <stdio.h>
#include <assert.h>

int main() {
    int num = 0;
    assert(num > 0); // 斷言失敗,觸發(fā)SIGABRT信號(hào)
    return 0;
}

由于num的值為 0,斷言assert(num > 0)失敗,程序會(huì)收到 SIGABRT 信號(hào),若未捕獲此信號(hào),就會(huì)產(chǎn)生 Core Dump。

(3)SIGFPE:該信號(hào)表示發(fā)生了致命的算術(shù)運(yùn)算錯(cuò)誤,例如除零操作。如下代碼:

#include <stdio.h>

int main() {
    int a = 10;
    int b = 0;
    int c = a / b; // 除零操作,觸發(fā)SIGFPE信號(hào)
    return 0;
}

這段代碼進(jìn)行了除零運(yùn)算,會(huì)觸發(fā) SIGFPE 信號(hào),在未捕獲處理的情況下,程序會(huì)終止并生成 Core Dump。

3.3資源限制與配置問(wèn)題

在 Linux 系統(tǒng)中,ulimit -c用于設(shè)置 Core 文件大小的上限。如果這個(gè)值被設(shè)置為 0(默認(rèn)情況下可能如此),那么即使程序崩潰觸發(fā)了 Core Dump 的生成條件,也不會(huì)產(chǎn)生 Core 文件。只有將ulimit -c設(shè)置為一個(gè)大于 0 的值,或者設(shè)置為unlimited(不限制大小),程序崩潰時(shí)才有可能生成 Core Dump 文件。比如,在終端中執(zhí)行ulimit -c 1024,表示將 Core 文件大小上限設(shè)置為 1024KB ,若程序崩潰產(chǎn)生的 Core 文件大小超過(guò)這個(gè)限制,可能無(wú)法完整生成。

當(dāng)程序崩潰需要生成 Core Dump 文件時(shí),如果磁盤(pán)空間不足,文件無(wú)法成功寫(xiě)入,也就無(wú)法生成有效的 Core Dump 文件。假設(shè)磁盤(pán)剩余空間只有 10MB,而程序崩潰時(shí)產(chǎn)生的 Core 文件預(yù)計(jì)大小為 20MB,那么就無(wú)法生成該 Core 文件,這會(huì)給我們后續(xù)調(diào)試程序帶來(lái)困難。

程序必須對(duì)生成 Core Dump 文件的目標(biāo)路徑具有寫(xiě)權(quán)限。例如,若程序嘗試在/var/core目錄下生成 Core 文件,但該程序運(yùn)行的用戶(hù)對(duì)/var/core目錄沒(méi)有寫(xiě)權(quán)限,就無(wú)法成功生成 Core Dump 文件。只有確保程序?qū)δ繕?biāo)路徑有正確的讀寫(xiě)權(quán)限,才能順利生成 Core 文件,以便后續(xù)分析調(diào)試。

3.4多線(xiàn)程問(wèn)題

在多線(xiàn)程環(huán)境下,程序的執(zhí)行流程變得更加復(fù)雜,一些潛在的問(wèn)題可能導(dǎo)致 Core Dump。

(1)競(jìng)態(tài)條件:當(dāng)多個(gè)線(xiàn)程同時(shí)訪問(wèn)和修改共享資源,并且沒(méi)有進(jìn)行適當(dāng)?shù)耐娇刂茣r(shí),就會(huì)出現(xiàn)競(jìng)態(tài)條件。這可能導(dǎo)致數(shù)據(jù)的不一致性,甚至程序崩潰并生成 Core Dump。比如,多個(gè)線(xiàn)程同時(shí)對(duì)一個(gè)全局變量進(jìn)行讀寫(xiě)操作,沒(méi)有加鎖保護(hù):

#include <iostream>
#include <thread>

int sharedVariable = 0;

void increment() {
    for (int i = 0; i < 1000; ++i) {
        sharedVariable++; // 多個(gè)線(xiàn)程同時(shí)訪問(wèn)和修改,沒(méi)有同步
    }
}

int main() {
    std::thread thread1(increment);
    std::thread thread2(increment);

    thread1.join();
    thread2.join();

    std::cout << "Final value of sharedVariable: " << sharedVariable << std::endl;
    return 0;
}

在這段代碼中,sharedVariable是共享資源,increment函數(shù)被兩個(gè)線(xiàn)程同時(shí)調(diào)用,由于沒(méi)有加鎖等同步機(jī)制,可能會(huì)出現(xiàn)競(jìng)態(tài)條件,導(dǎo)致程序運(yùn)行結(jié)果不確定,甚至可能引發(fā) Core Dump。

(2)死鎖:當(dāng)兩個(gè)或多個(gè)線(xiàn)程相互等待對(duì)方釋放資源,形成一種僵持的狀態(tài),就發(fā)生了死鎖。死鎖會(huì)使程序無(wú)法繼續(xù)執(zhí)行,最終可能導(dǎo)致 Core Dump。以下是一個(gè)簡(jiǎn)單的死鎖示例:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1;
std::mutex mutex2;

void threadFunction1() {
    mutex1.lock();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    mutex2.lock(); // 等待mutex2,而mutex2被threadFunction2持有
    mutex2.unlock();
    mutex1.unlock();
}

void threadFunction2() {
    mutex2.lock();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    mutex1.lock(); // 等待mutex1,而mutex1被threadFunction1持有
    mutex1.unlock();
    mutex2.unlock();
}

int main() {
    std::thread thread1(threadFunction1);
    std::thread thread2(threadFunction2);

    thread1.join();
    thread2.join();

    return 0;
}

在這個(gè)例子中,threadFunction1和threadFunction2相互等待對(duì)方持有的鎖,形成死鎖,程序會(huì)陷入停滯,最終可能崩潰產(chǎn)生 Core Dump。

3.5動(dòng)態(tài)內(nèi)存管理錯(cuò)誤

(1)雙重釋放:在 C/C++ 中,當(dāng)對(duì)一塊已經(jīng)釋放的內(nèi)存再次調(diào)用釋放函數(shù)(如free或delete)時(shí),就會(huì)發(fā)生雙重釋放。這是一種未定義行為,可能導(dǎo)致程序崩潰并生成 Core Dump。例如:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(sizeof(int));
    free(ptr);
    free(ptr); // 雙重釋放,會(huì)導(dǎo)致未定義行為,可能引發(fā)Core Dump
    return 0;
}

這段代碼中,ptr指向的內(nèi)存被釋放了兩次,這是非常危險(xiǎn)的操作,極有可能導(dǎo)致程序出現(xiàn)異常,進(jìn)而產(chǎn)生 Core Dump。

(2)內(nèi)存泄漏:雖然內(nèi)存泄漏本身不會(huì)直接導(dǎo)致 Core Dump,但隨著程序的長(zhǎng)時(shí)間運(yùn)行,不斷泄漏的內(nèi)存會(huì)逐漸耗盡系統(tǒng)資源。當(dāng)系統(tǒng)沒(méi)有足夠的內(nèi)存供程序使用時(shí),程序就可能會(huì)崩潰并生成 Core Dump。比如在下面的 C++ 代碼中:

#include <iostream>

void memoryLeakFunction() {
    while (true) {
        int *ptr = new int; // 不斷分配內(nèi)存,但沒(méi)有釋放
    }
}

int main() {
    memoryLeakFunction();
    return 0;
}

memoryLeakFunction函數(shù)中不斷使用new分配內(nèi)存,卻沒(méi)有使用delete釋放,隨著循環(huán)的進(jìn)行,內(nèi)存會(huì)不斷被消耗,最終可能導(dǎo)致程序因內(nèi)存不足而崩潰,生成 Core Dump。

3.6程序邏輯錯(cuò)誤

(1)無(wú)限遞歸:當(dāng)一個(gè)函數(shù)不斷調(diào)用自身,沒(méi)有終止條件時(shí),就會(huì)發(fā)生無(wú)限遞歸。每一次遞歸調(diào)用都會(huì)在棧上分配新的空間,隨著遞歸深度的增加,??臻g會(huì)被耗盡,導(dǎo)致棧溢出,進(jìn)而引發(fā) Core Dump。例如:

#include <stdio.h>

void recursiveFunction() {
    recursiveFunction(); // 無(wú)限遞歸,會(huì)導(dǎo)致棧溢出
}

int main() {
    recursiveFunction();
    return 0;
}

在這段代碼中,recursiveFunction函數(shù)沒(méi)有任何終止條件,會(huì)一直遞歸調(diào)用自身,最終導(dǎo)致棧溢出,程序崩潰并生成 Core Dump。

(2)未捕獲異常:在 C++ 等支持異常處理的語(yǔ)言中,如果程序拋出了異常,但沒(méi)有在合適的地方捕獲并處理它,異常會(huì)向上層傳遞。如果最終沒(méi)有被捕獲,程序就會(huì)異常終止,可能生成 Core Dump。例如:

#include <iostream>

void functionThatThrows() {
    throw 1; // 拋出異常
}

int main() {
    try {
        functionThatThrows();
    } catch (...) {
        // 這里沒(méi)有捕獲到異常,異常會(huì)繼續(xù)向上傳遞
    }
    return 0;
}

在這段代碼中,functionThatThrows函數(shù)拋出了一個(gè)異常,但在main函數(shù)中沒(méi)有被正確捕獲,異常會(huì)導(dǎo)致程序異常終止,有很大概率生成 Core Dump。

3.7硬件問(wèn)題

雖然相對(duì)較少見(jiàn),但硬件問(wèn)題也可能引發(fā) Core Dump。

  • 內(nèi)存故障:當(dāng)計(jì)算機(jī)的物理內(nèi)存出現(xiàn)故障時(shí),程序在訪問(wèn)內(nèi)存時(shí)可能會(huì)出現(xiàn)錯(cuò)誤。比如內(nèi)存中的某些存儲(chǔ)單元損壞,導(dǎo)致讀取或?qū)懭霐?shù)據(jù)時(shí)出現(xiàn)異常,這可能觸發(fā) SIGSEGV 等信號(hào),進(jìn)而使程序崩潰并生成 Core Dump。
  • CPU 異常:CPU 出現(xiàn)硬件故障或者過(guò)熱等異常情況時(shí),也可能影響程序的正常執(zhí)行。例如,CPU 在執(zhí)行指令時(shí)發(fā)生錯(cuò)誤,無(wú)法正確處理程序的請(qǐng)求,這可能導(dǎo)致程序崩潰,生成 Core Dump 文件 。不過(guò),這類(lèi)由硬件問(wèn)題導(dǎo)致的 Core Dump,排查起來(lái)相對(duì)困難,需要結(jié)合硬件檢測(cè)工具來(lái)確定具體原因。

四、Core Dump 的分析與調(diào)試

4.1使用 GDB 分析 Core Dump 文件

當(dāng)程序崩潰生成 Core Dump 文件后,我們就可以使用 GDB(GNU 調(diào)試器)來(lái)分析它,從而找出程序崩潰的原因。GDB 是一個(gè)功能強(qiáng)大的調(diào)試工具,在 Linux 系統(tǒng)下被廣泛應(yīng)用于程序調(diào)試。

假設(shè)我們有一個(gè)名為my_program的可執(zhí)行文件,以及它崩潰時(shí)生成的core.1234的 Core Dump 文件。首先,我們需要打開(kāi)終端,進(jìn)入到 Core Dump 文件和可執(zhí)行文件所在的目錄。然后,在終端中輸入以下命令,使用 GDB 加載 Core Dump 文件和可執(zhí)行文件:

gdb ./my_program core.1234

執(zhí)行上述命令后,GDB 會(huì)加載相關(guān)信息,并顯示一些關(guān)于程序崩潰的初步信息,比如程序是因?yàn)榻邮盏绞裁葱盘?hào)而崩潰的。

接下來(lái),我們使用bt(backtrace 的縮寫(xiě))命令來(lái)查看程序崩潰時(shí)的調(diào)用棧。調(diào)用棧就像是程序執(zhí)行路徑的 “歷史記錄”,它展示了從程序入口開(kāi)始,到崩潰點(diǎn)之前,函數(shù)的調(diào)用順序。通過(guò)分析調(diào)用棧,我們可以了解程序的執(zhí)行流程,進(jìn)而定位到崩潰發(fā)生的具體函數(shù)和代碼行。在 GDB 的命令行中輸入bt,然后回車(chē),GDB 會(huì)輸出類(lèi)似如下的內(nèi)容:

#0  0x00007ffff7a0c397 in strlen () from /lib64/libc.so.6
#1  0x000000000040055d in main () at test.c:5

從這個(gè)輸出中,我們可以看到,程序在test.c文件的第 5 行發(fā)生了崩潰,當(dāng)時(shí)正在調(diào)用strlen函數(shù)。這就為我們定位問(wèn)題提供了重要線(xiàn)索,我們可以進(jìn)一步查看test.c文件的第 5 行及其相關(guān)代碼,來(lái)分析為什么會(huì)在這里崩潰。

除了bt命令,GDB 還有許多其他有用的命令,比如info locals可以查看當(dāng)前函數(shù)的局部變量,print命令可以打印變量的值,list命令可以列出源代碼等 。這些命令結(jié)合使用,能夠幫助我們更全面、深入地分析 Core Dump 文件,找出程序崩潰的根本原因。例如,我們可以使用print命令來(lái)查看某個(gè)變量在崩潰時(shí)的值,假設(shè)在上述崩潰案例中,我們想查看test.c文件中第 5 行涉及的某個(gè)變量str的值,可以在 GDB 中輸入print str,GDB 會(huì)輸出該變量的值,這有助于我們判斷變量是否符合預(yù)期,從而進(jìn)一步分析問(wèn)題。

4.2檢查系統(tǒng)配置

在進(jìn)行 Core Dump 分析之前,確保系統(tǒng)配置正確是非常重要的,這直接影響到我們能否成功生成和分析 Core Dump 文件。

(1)ulimit -c 設(shè)置:正如前面提到的,ulimit -c用于設(shè)置 Core 文件大小的上限。如果這個(gè)值被設(shè)置為 0,程序崩潰時(shí)將不會(huì)生成 Core 文件。因此,我們需要確保ulimit -c的值不是 0,最好設(shè)置為unlimited,以允許生成完整的 Core 文件??梢酝ㄟ^(guò)以下命令來(lái)檢查當(dāng)前的ulimit -c設(shè)置:

ulimit -c

如果輸出為 0,我們可以使用以下命令將其設(shè)置為不限制大?。?/span>

ulimit -c unlimited

需要注意的是,ulimit命令的設(shè)置通常只對(duì)當(dāng)前終端會(huì)話(huà)有效。如果希望永久生效,可以將相關(guān)設(shè)置添加到用戶(hù)的配置文件(如~/.bashrc或~/.zshrc)中。

(2)Core Dump 存儲(chǔ)路徑權(quán)限:程序必須對(duì)生成 Core Dump 文件的目標(biāo)路徑具有寫(xiě)權(quán)限。如果路徑權(quán)限不足,即使程序崩潰觸發(fā)了 Core Dump 生成條件,也無(wú)法生成有效的 Core 文件。比如,我們?cè)?var/core目錄下生成 Core 文件,就需要確保運(yùn)行程序的用戶(hù)對(duì)/var/core目錄有寫(xiě)權(quán)限。可以使用以下命令來(lái)檢查目錄權(quán)限:

ls -l /var/core

如果發(fā)現(xiàn)權(quán)限不足,可以使用chmod命令來(lái)修改權(quán)限,例如:

sudo chmod 777 /var/core

這樣,所有用戶(hù)都對(duì)/var/core目錄具有讀、寫(xiě)和執(zhí)行權(quán)限,確保程序能夠在該目錄下成功生成 Core Dump 文件 。另外,還需要保證生成 Core Dump 文件的路徑所在磁盤(pán)有足夠的空間,避免因磁盤(pán)空間不足導(dǎo)致 Core 文件無(wú)法生成。

4.3GDB 加載 Core Dump 文件

在 Linux 系統(tǒng)中,GDB(GNU Debugger)是一款強(qiáng)大的調(diào)試工具,在分析 Core Dump 文件時(shí)發(fā)揮著關(guān)鍵作用。使用 GDB 加載可執(zhí)行文件和 Core Dump 文件的操作相對(duì)簡(jiǎn)單。假設(shè)我們有一個(gè)名為my_program的可執(zhí)行文件,以及對(duì)應(yīng)的 Core Dump 文件core.1234(這里的1234為進(jìn)程 ID,實(shí)際使用時(shí)需根據(jù)具體情況替換),在終端中輸入以下命令即可啟動(dòng) GDB 并加載相關(guān)文件:

gdb my_program core.1234

執(zhí)行該命令后,GDB 會(huì)自動(dòng)加載可執(zhí)行文件和 Core Dump 文件,并停留在程序崩潰時(shí)的位置。此時(shí),我們就可以利用 GDB 提供的各種命令對(duì) Core Dump 進(jìn)行深入分析,探尋程序崩潰的原因。

①where/bt—— 查看堆棧信息

在 GDB 中,where和bt(backtrace 的縮寫(xiě))命令功能相近,主要用于查看當(dāng)前線(xiàn)程的函數(shù)調(diào)用堆棧信息。這就像是沿著程序崩潰時(shí)的 “足跡”,一步步回溯到程序的入口點(diǎn),幫助我們清晰地了解程序執(zhí)行的路徑,從而找到問(wèn)題所在。

當(dāng)程序崩潰時(shí),使用bt命令,GDB 會(huì)輸出函數(shù)調(diào)用的序列,每一行都包含了函數(shù)名、所在文件以及行號(hào)等重要信息。例如:

(gdb) bt
#0  func3 (arg1=0x7fffffffde10, arg2=42) at my_file.c:123
#1  0x00005555555552b5 in func2 (arg=0x7fffffffde10) at main.c:234
#2  0x0000555555555350 in main () at main.c:345

從上述輸出中可以看出,程序崩潰時(shí)正在執(zhí)行func3函數(shù),該函數(shù)位于my_file.c文件的第 123 行,而func3是由func2調(diào)用的,func2又在main函數(shù)中被調(diào)用。通過(guò)這樣的堆棧信息,我們能夠快速定位到程序崩潰的大致位置,進(jìn)而深入分析問(wèn)題。

②p—— 查看變量值

p(print 的縮寫(xiě))命令用于打印變量的值,這在分析 Core Dump 時(shí)非常實(shí)用。通過(guò)查看變量在程序崩潰時(shí)的值,可以判斷程序的運(yùn)行狀態(tài)是否符合預(yù)期,從而發(fā)現(xiàn)潛在的問(wèn)題。

例如,我們懷疑某個(gè)變量在程序崩潰時(shí)的值異常,可使用p命令查看其值。假設(shè)我們要查看變量my_variable的值,在 GDB 中輸入:

(gdb) p my_variable

GDB 會(huì)輸出my_variable的值。如果該變量是一個(gè)復(fù)雜的數(shù)據(jù)結(jié)構(gòu),如結(jié)構(gòu)體或數(shù)組,p命令也能以相應(yīng)的格式展示其內(nèi)容。例如,對(duì)于一個(gè)結(jié)構(gòu)體變量my_struct,輸入p my_struct,GDB 會(huì)顯示結(jié)構(gòu)體中各個(gè)成員的值。這有助于我們?nèi)媪私獬绦虮罎r(shí)變量的狀態(tài),為問(wèn)題排查提供有力支持。

③ info registers—— 查看寄存器信息

info registers命令用于顯示當(dāng)前寄存器的內(nèi)容。寄存器是 CPU 中用于臨時(shí)存儲(chǔ)數(shù)據(jù)的高速存儲(chǔ)單元,程序運(yùn)行過(guò)程中的各種數(shù)據(jù)處理和指令執(zhí)行都與寄存器密切相關(guān)。通過(guò)查看寄存器在程序崩潰時(shí)的狀態(tài),我們可以獲取更多關(guān)于程序運(yùn)行的底層信息,這對(duì)于深入分析程序崩潰原因至關(guān)重要。

在 GDB 中輸入info registers,會(huì)輸出一系列寄存器及其對(duì)應(yīng)的值。例如:

(gdb) info registers
rax            0x0      0
rbx            0x7ffff7fc1a40   140737351884352
rcx            0x1      1
rdx            0x7ffff7bc8723   140737351871779
...

這些寄存器的值反映了程序崩潰瞬間 CPU 的工作狀態(tài),結(jié)合其他調(diào)試信息,能夠幫助我們更全面地理解程序崩潰的原因,尤其是在涉及到硬件相關(guān)的問(wèn)題時(shí),寄存器信息的分析尤為重要。

五、Core Dump實(shí)戰(zhàn)案例

5.1簡(jiǎn)單案例分析

下面通過(guò)一個(gè)簡(jiǎn)單的代碼示例,來(lái)看看如何利用 GDB 對(duì) Core Dump 進(jìn)行分析。假設(shè)有如下一段 C 語(yǔ)言代碼:

#include <stdio.h>
#include <stdlib.h>

void func() {
    int *ptr = NULL;
    *ptr = 10; // 這里會(huì)導(dǎo)致空指針解引用,引發(fā)Core Dump
}

int main() {
    func();
    return 0;
}

在上述代碼中,func函數(shù)內(nèi)定義了一個(gè)空指針ptr,并嘗試對(duì)其進(jìn)行解引用操作,這必然會(huì)引發(fā)程序崩潰。為了讓 GDB 能夠更好地分析問(wèn)題,在編譯時(shí)需要加上-g選項(xiàng),以生成調(diào)試信息。編譯命令如下:

gcc -g -o test test.c

運(yùn)行該程序后,程序會(huì)因?yàn)榭罩羔樈庖枚罎?,并生?Core Dump 文件(前提是已正確配置 Core Dump 生成,如設(shè)置ulimit -c unlimited)。假設(shè)生成的 Core Dump 文件名為core.12345(12345為進(jìn)程 ID)。

接下來(lái),使用 GDB 加載可執(zhí)行文件和 Core Dump 文件進(jìn)行分析:

gdb test core.12345

進(jìn)入 GDB 環(huán)境后,使用bt命令查看堆棧信息:

(gdb) bt
#0  func () at test.c:5
#1  0x0000555555555199 in main () at test.c:9

從輸出結(jié)果可以清晰地看到,程序在test.c文件的第 5 行發(fā)生崩潰,此時(shí)正在執(zhí)行func函數(shù),而func函數(shù)是由main函數(shù)調(diào)用的。再使用p命令查看ptr變量的值:

(gdb) p ptr
$1 = (int *) 0x0

由此可知,ptr確實(shí)是一個(gè)空指針,這就是導(dǎo)致程序崩潰并產(chǎn)生 Core Dump 的原因。通過(guò)這個(gè)簡(jiǎn)單的案例,我們初步領(lǐng)略了 GDB 在分析 Core Dump 文件時(shí)的強(qiáng)大功能,它能夠快速準(zhǔn)確地定位到問(wèn)題所在,為開(kāi)發(fā)者節(jié)省大量的調(diào)試時(shí)間。

5.2復(fù)雜場(chǎng)景實(shí)戰(zhàn)

在實(shí)際的項(xiàng)目開(kāi)發(fā)中,多線(xiàn)程程序的 Core Dump 問(wèn)題往往更加復(fù)雜和難以排查。下面分享一個(gè)多線(xiàn)程程序出現(xiàn) Core Dump 的案例,以及如何運(yùn)用 GDB 及多線(xiàn)程調(diào)試命令來(lái)解決問(wèn)題。

假設(shè)有一個(gè)多線(xiàn)程程序,其功能是多個(gè)線(xiàn)程同時(shí)對(duì)一個(gè)共享數(shù)組進(jìn)行讀寫(xiě)操作。部分代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define ARRAY_SIZE 100
int shared_array[ARRAY_SIZE];
pthread_mutex_t mutex;

void *write_thread(void *arg) {
    for (int i = 0; i < ARRAY_SIZE; i++) {
        pthread_mutex_lock(&mutex);
        shared_array[i] = i;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

void *read_thread(void *arg) {
    for (int i = 0; i < ARRAY_SIZE; i++) {
        pthread_mutex_lock(&mutex);
        int value = shared_array[i];
        printf("Read value: %d at index %d\n", value, i);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t write_tid, read_tid;
    pthread_mutex_init(&mutex, NULL);

    if (pthread_create(&write_tid, NULL, write_thread, NULL)!= 0) {
        perror("Failed to create write thread");
        return 1;
    }
    if (pthread_create(&read_tid, NULL, read_thread, NULL)!= 0) {
        perror("Failed to create read thread");
        return 1;
    }

    if (pthread_join(write_tid, NULL)!= 0) {
        perror("Failed to join write thread");
        return 1;
    }
    if (pthread_join(read_tid, NULL)!= 0) {
        perror("Failed to join read thread");
        return 1;
    }

    pthread_mutex_destroy(&mutex);
    return 0;
}

在這個(gè)程序中,我們創(chuàng)建了一個(gè)寫(xiě)線(xiàn)程和一個(gè)讀線(xiàn)程,它們通過(guò)互斥鎖mutex來(lái)保證對(duì)共享數(shù)組shared_array的安全訪問(wèn)。然而,在實(shí)際運(yùn)行過(guò)程中,程序偶爾會(huì)出現(xiàn) Core Dump 現(xiàn)象。

為了調(diào)試這個(gè)問(wèn)題,首先確保在編譯時(shí)加上-g選項(xiàng),以生成調(diào)試信息:

gcc -g -o multi_thread_test multi_thread_test.c -lpthread

運(yùn)行程序后,當(dāng) Core Dump 發(fā)生時(shí),假設(shè)生成的 Core Dump 文件名為core.67890。使用 GDB 加載可執(zhí)行文件和 Core Dump 文件:

gdb multi_thread_test core.67890

進(jìn)入 GDB 環(huán)境后,首先使用info threads命令查看所有線(xiàn)程的信息:

(gdb) info threads
  Id   Target Id         Frame
  1    Thread 0x7ffff7fda700 (LWP 67890) "multi_thread_test" main () at multi_thread_test.c:32
  2    Thread 0x7ffff77ef700 (LWP 67891) "multi_thread_test" read_thread (arg=0x0) at multi_thread_test.c:18
  3    Thread 0x7ffff6fee700 (LWP 67892) "multi_thread_test" write_thread (arg=0x0) at multi_thread_test.c:10

從輸出結(jié)果可以看到,程序中有三個(gè)線(xiàn)程,其中線(xiàn)程 1 是主線(xiàn)程,線(xiàn)程 2 是讀線(xiàn)程,線(xiàn)程 3 是寫(xiě)線(xiàn)程。接下來(lái),我們需要切換到發(fā)生問(wèn)題的線(xiàn)程進(jìn)行分析。假設(shè)通過(guò)觀察,發(fā)現(xiàn)線(xiàn)程 2 在讀取共享數(shù)組時(shí)出現(xiàn)了 Core Dump。使用thread 2命令切換到線(xiàn)程 2:

(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff77ef700 (LWP 67891))]
#0  read_thread (arg=0x0) at multi_thread_test.c:20

此時(shí),我們已經(jīng)切換到讀線(xiàn)程,并且 GDB 停在了讀線(xiàn)程發(fā)生問(wèn)題的代碼行。使用bt命令查看讀線(xiàn)程的堆棧信息:

(gdb) bt
#0  read_thread (arg=0x0) at multi_thread_test.c:20
#1  0x00007ffff7bc8723 in pthread_mutex_lock () from /lib/x86_64-linux-gnu/libpthread.so.0
#2  0x00005555555552b5 in main () at multi_thread_test.c:28

從堆棧信息可以看出,讀線(xiàn)程在執(zhí)行pthread_mutex_lock函數(shù)時(shí)出現(xiàn)了問(wèn)題。進(jìn)一步使用p命令查看相關(guān)變量的值,例如查看i的值:

(gdb) p i
$1 = 120

發(fā)現(xiàn)i的值超出了共享數(shù)組的邊界ARRAY_SIZE(這里ARRAY_SIZE為 100),這就是導(dǎo)致 Core Dump 的原因。原來(lái)是在多線(xiàn)程環(huán)境下,由于線(xiàn)程調(diào)度的不確定性,讀線(xiàn)程在寫(xiě)線(xiàn)程尚未完全初始化共享數(shù)組時(shí),就嘗試讀取了越界的位置,從而引發(fā)了錯(cuò)誤。通過(guò)這個(gè)復(fù)雜場(chǎng)景的實(shí)戰(zhàn)案例,我們可以看到,在多線(xiàn)程程序中,利用 GDB 的多線(xiàn)程調(diào)試命令,能夠逐步排查出 Core Dump 的根源,為解決復(fù)雜的多線(xiàn)程問(wèn)題提供了有力的手段。

責(zé)任編輯:武曉燕 來(lái)源: 深度Linux
相關(guān)推薦

2010-06-02 09:31:43

Linux core

2021-04-20 09:52:43

Linuxcore dump代碼

2020-09-11 16:17:02

產(chǎn)品定價(jià)AI人工智能

2025-05-20 08:40:00

2010-06-09 08:39:34

2010-04-07 16:50:41

雙線(xiàn)解析DNS巧搭建

2021-05-01 20:36:01

隨身WiFiWiFi網(wǎng)絡(luò)

2011-12-29 11:57:08

WA2612無(wú)線(xiàn)信號(hào)

2015-07-06 11:57:18

移動(dòng)設(shè)備安全移動(dòng)安全策略

2017-08-16 15:11:10

ELK集群監(jiān)控

2019-12-26 09:52:33

Redis集群線(xiàn)程

2020-10-27 10:43:24

Redis字符串數(shù)據(jù)庫(kù)

2011-12-08 10:25:19

戴爾IT設(shè)備升級(jí)解決方案

2009-08-27 16:53:05

C# using作用

2021-10-20 18:46:45

人工智能AI無(wú)人機(jī)

2009-04-29 14:46:15

ADSL寬帶掉線(xiàn)

2010-08-13 09:50:53

桌面虛擬化

2025-04-25 10:00:00

2010-06-02 09:01:20

Linux core
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)