Ubuntu+GCC(g++):Program received signal SIGSEGV, Segmentation fault

本文记录了一次解决Linux程序段错误的经历,通过逐步排查和修改代码结构,最终定位到问题根源在于同一变量在不同源文件中有不同的定义实现。


又是这个问题!估计又是stl库的string在作怪。还不如自己写一个简单的string代替stl的string。

http://topic.youkuaiyun.com/u/20081009/15/b7df31b4-d625-4091-9f09-0be8775a66bf.html

不是这个原因,已经加入了pthread。

现在的情况是,代码过于复杂,没法注释掉一部分代码来调试。而每次调试也得操作几十秒,很耽误时间,所以,最好的方法是,找出所有可能的原因,然后逐个地排除。

此前没有问题。加入一部分代码后,出现问题,但是,此前这部分是没有问题的。所以,可以排除栈溢出的可能性,以为栈溢出应该是在局部范围内出现的。于是,很可能是堆溢出。

Program received signal SIGSEGV, Segmentation fault.
At /mnt/share/Server/common/TLLock/TL_Lock.h:80


class CTL_AutoLock
{
protected:
I_TL_Lock * m_pLock;

public:
CTL_AutoLock(I_TL_Lock * plock)
{
m_pLock=plock;

m_pLock->Lock(); //提示出错处
};

virtual ~CTL_AutoLock()
{
m_pLock->Unlock();
}

};

反汇编:

0x804d56c push ebp
0x804d56d mov ebp,esp
0x804d56f sub esp,0x18
0x804d572 mov eax,DWORD PTR [ebp+0x8]
0x804d575 mov DWORD PTR [eax],0x8066720
0x804d57b mov eax,DWORD PTR [ebp+0x8]
0x804d57e mov edx,DWORD PTR [ebp+0xc]
0x804d581 mov DWORD PTR [eax+0x4],edx
0x804d584 mov eax,DWORD PTR [ebp+0x8]
0x804d587 mov eax,DWORD PTR [eax+0x4]
0x804d58a mov eax,DWORD PTR [eax]
0x804d58c mov edx,DWORD PTR [eax] //就是这儿了
0x804d58e mov eax,DWORD PTR [ebp+0x8]
0x804d591 mov eax,DWORD PTR [eax+0x4]
0x804d594 mov DWORD PTR [esp+0x4],0x0
0x804d59c mov DWORD PTR [esp],eax
0x804d59f call edx
0x804d5a1 leave
0x804d5a2 ret

寄存器:

eax是0,edx是0x8081990.

也就等同于edx=*((DWORD *)NULL);,不崩溃是不可能的了。

解决!

原因是大量使用inline内联函数,导致堆栈溢出!一年半以前,在VC平台下遇到类似的问题。

现在,我能深刻地体会到书本上面的那句话,内联函数应该尽量短小。否则,不但不能加快程序的运行速度,反而可能会导致栈溢出。

然而,问题还是存在的。某些小函数还是使用了内联的,小函数中再调用小函数(也是内联),结果,又出现上面的崩溃现象。

http://blog.youkuaiyun.com/kankanli/archive/2009/10/30/4748947.aspx

继续。虽然此前去除了很多inline函数,解决了部分问题。但是最根本的问题并没有解决。我把这些内联的小函数改为非内联的,问题依旧。这说明,确确实实存在堆溢出!而不是上面的内联导致的栈溢出。

Linux C中令人讨厌的段错误

http://tieba.baidu.com/f?kz=670490198

另外一个程序,gdb调试的时候,基本上不会出问题,但是,一旦直接运行,则也会出现断错误!还会偶尔出现“总线错误”,也会出现double list那个重复回收内存的错误。

总线错误——我这里是因为磁盘空间不足,回收一部分垃圾文件,之后就不再提示。

彻底解决!

此次错误完全是我自己造成的。我写了这样的代码:

common.h文件里面:

struct _THREAD_PARAM;

typedef _THREAD_PARAM THREAD_PARAM;

typedef _THREAD_PARAM * P_THREAD_PARAM;

A.h里面:

class A

{

P_THREAD_PARAM m_param;

};

A.cpp 里面:

struct _THREAD_PARAM

{

......

};

B.h里面:

class B

{

P_THREAD_PARAM m_param;

};

B.cpp 里面:

struct _THREAD_PARAM

{

......

};

简单地说,就是,我使用了一个变量声明,在不同的cpp里面,使用不同的实现体。

因为我没有看过编译原理这些东西了,所以,我的理解是,这些变量位于不同的cpp里面,编译之后应该是不同的代码段中,所以,具体实现无所谓了的,变量只是一个名称嘛。

但是没有想到,正是这个错误,自八月份至今,导致程序一直有崩溃问题,这次算是真正解决了!

我就说嘛,我写程序还是很规矩的,自己分配的内存必然回收,自己拉的××必然自己处理,写程序和为人处事是一样的。所以,不应该出现什么段错误啊。这次,也是由于偷懒,因为我实在不想多写几行代码、多定义几个变量名称,一个人写的程序反映了一个人的性格。

呵呵,总算解决了。

<think>好的,用户问的是如何在GDB中查看收到SIGSEGV信号后的具体信息。我需要详细解释步骤,可能还要包括示例和注意事项。 首先,用户可能遇到了段错误,想通过GDB调试找到原因。他们可能已经知道要使用GDB,但需要具体的命令和步骤。我应该从启动程序开始,比如用gdb加载可执行文件,然后运行直到崩溃。 接下来,用户可能需要查看崩溃时的堆栈跟踪,所以需要介绍bt命令。然后,检查寄存器状态,特别是RIP或EIP,这样他们可以知道崩溃时的指令地址。查看汇编代码可能会有帮助,所以用disassemble命令,并结合附近的源代码行。 信息寄存器如RSP和RBP也很重要,可能涉及内存访问错误。查看变量的值和内存地址是否正确,比如使用x命令检查指针是否有效。如果错误是空指针或越界,这些步骤能帮助确认。 用户可能还不太清楚如何分析堆栈帧,所以需要解释frame和info locals的使用。此外,可能需要注意编译时是否带有调试信息,如果没有,GDB的输出可能不够详细,所以要提醒他们编译时加上-g选项。 另外,可能要考虑内存布局问题,比如查看/proc/pid/maps,或者使用GDB的vmmap命令(如果支持)。还有,信号处理的问题,比如是否程序有自己的SIGSEGV处理函数,这可能会影响GDB的行为。 最后,用户可能需要一些高级技巧,比如反向调试或者使用core dump文件,但可能超出当前问题的范围。需要保持回答集中在直接查看崩溃后的信息上,确保步骤清晰,容易跟随。 可能还需要提醒常见错误,比如野指针、缓冲区溢出、使用已释放内存等,并给出检查这些问题的建议。确保用户了解每一步的目的,以便他们能自主诊断问题。</think>以下是使用GDB分析`SIGSEGV`段错误的核心步骤和技巧: --- ### **1. 基础调试流程** ```bash # 编译时必须包含调试信息(-g) gcc -g -o test test.c # 启动GDB调试 gdb ./test # 运行程序直到崩溃 (gdb) run ``` --- ### **2. 关键信息获取命令** #### **2.1 查看崩溃位置** ```gdb (gdb) bt full # 完整堆栈回溯(含局部变量值) (gdb) info frame # 当前栈帧详细信息 (gdb) disassemble /m # 混合显示源代码和汇编 ``` #### **2.2 寄存器分析** ```gdb (gdb) info registers # 查看所有寄存器 (gdb) p $rip # 查看崩溃时的指令指针(x86_64) (gdb) x/i $rip # 查看崩溃位置的汇编指令 ``` #### **2.3 内存访问验证** ```gdb (gdb) p/x (void*)0x7ffeeb4a9ff0 # 检查错误地址是否合法 (gdb) x/8wx $rsp # 查看栈顶内存内容 (gdb) info proc mappings # 查看进程内存映射(Linux) ``` --- ### **3. 典型场景分析** #### **场景1:空指针解引用** ```gdb Program received signal SIGSEGV, Segmentation fault. 0x0000555555555169 in main () at test.c:8 8 printf("%d\n", *ptr); # ptr为NULL (gdb) p ptr # 显示ptr=0x0 (gdb) x/10x ptr # 显示Cannot access memory at address 0x0 ``` #### **场景2:栈溢出** ```gdb (gdb) bt #0 0x00005555555551a9 in overflow (depth=32767) at stack.c:6 #1 0x00005555555551c2 in overflow (depth=32766) at stack.c:7 ... # 观察到大量重复栈帧 ``` #### **场景3:堆内存越界** ```gdb (gdb) p *(malloc_usable_size(ptr)) # 查看实际分配的内存大小 (gdb) watch *(int*)0x7ffff7ebc000 # 设置内存断点 ``` --- ### **4. 高级调试技巧** #### **4.1 反向调试(record)** ```gdb (gdb) record # 启动执行记录 (gdb) reverse-step # 反向执行到崩溃前 ``` #### **4.2 核心转储分析** ```bash ulimit -c unlimited # 启用核心转储 ./test # 触发崩溃生成core文件 gdb ./test core # 加载核心转储 ``` #### **4.3 内存安全检查** ```gdb (gdb) set environment LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libasan.so (gdb) run # 启用AddressSanitizer ``` --- ### **5. 常见错误模式速查表** | 错误特征 | 可能原因 | 验证方法 | |------------------------------|---------------------------|------------------------------| | 地址0x0访问 | 空指针解引用 | `info symbol 0x地址` | | 地址对齐错误(e.g. 0x12345f) | 未对齐访问 | `p/x $rax & 0x7` | | 地址在堆区间 | 堆内存越界/重复释放 | `info proc mappings` | | 地址在栈区间 | 栈溢出/局部变量越界 | `x/32x $rsp` | | 地址在代码段 | 函数指针错误 | `disassemble 0x地址` | --- ### **6. 内存布局分析示例** ```gdb (gdb) info proc mappings Start Addr End Addr Size Offset Perms objfile 0x555555554000 0x555555555000 0x1000 0x0 r--p /path/to/test 0x555555555000 0x555555556000 0x1000 0x1000 r-xp /path/to/test # 代码段 0x7ffff7dd8000 0x7ffff7dfd000 0x25000 0x0 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7ffffffde000 0x7ffffffff000 0x21000 0x0 rw-p [stack] # 栈空间 ``` --- ### **7. 自动化分析脚本** ```gdb define analyze_segfault echo === Stack Trace ===\n bt full echo === Registers ===\n info registers echo === Fault Address ===\n info symbol $addr echo === Memory Map ===\n info proc mappings end ``` --- ### **关键注意事项** 1. **调试符号必须存在**:编译时使用`-g`选项 2. **优化影响**:-O2优化可能导致变量不可见,建议使用`-O0`调试 3. **动态库问题**:使用`LD_LIBRARY_PATH`确保加载正确库版本 4. **线程调试**:`info threads`查看所有线程状态 5. **硬件断点**:`watch -l *(int*)0x7ffffffde000`监控内存变化 通过上述方法,可以准确定位到导致SIGSEGV的具体代码位置、内存操作类型(读/写)以及非法内存地址的来源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值