段错误(核心已转储)(core dumped)问题的分析方法
段错误(核心已转储)(core dumped)问题的分析方法
1.段错误
1.1段错误的含义
段错误就是指访问的内存超出了系统所给这个程序的内存空间,通常这个值是由gd tr来保存的,他是一个48位的寄存器,其中的32位是保存由它指向的 gdt表,后13位保存 相应于gdt的下标,最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向 的gdt是由以64位为一个单位的表,在这张表中就保存着程序运行的代码段以及数据段的起 始地址以及与此相应的段限和页面交换还有程序运行级别还有内存粒度等等的信息。
英文介绍:A segmentation fault (often shortened to segfault) is a particular error condition that can occur during the operation of computer software. In short, a segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (e.g., attempts to write to a read-only location, or to overwrite part of the operating system). Systems based on processors like the Motorola 68000 tend to refer to these events as Address or Bus errors.
Segmentation is one approach to memory management and protection in the operating system. It has been superseded by paging for most purposes, but much of the terminology of segmentation is still used, “segmentation fault” being an example. Some operating systems still have segmentation at some logical level although paging is used as the main memory management policy.
On Unix-like operating systems, a process that accesses invalid memory receives the SIGSEGV signal. On Microsoft Windows, a process that accesses invalid memory receives the STATUS_ACCESS_VIOLATION exception.
意思就是
一旦一个程序发生了越界访问,cpu就会产生相应的保护,于是segmentation fault就出现
了通过上面的解释,段错误应该就是访问了不可访问的内存,这个内存区要么是不存在的,
要么是受到系统保护的,还有可能是缺少文件或者文件损坏。
1.2实际常见原因
在编程中以下几类做法容易导致段错误,基本是是错误地使用指针引起的:
1)访问系统数据区,尤其是往系统保护的内存地址写数据,最常见的就是给一个指针以0地址;
2)内存越界(数组越界,变量类型不一致等) 访问到不属于你的内存区域。
另 外,缓存溢出也可能引起“段错误”,对于这种while(1) {do}的程序,这个问题最容易发生,多此sprintf或着strcat有可能将某个buff填满,溢出,所以每次使用前,最好memset一下,不过 要是一开始就是段错误,而不是运行了一会儿出现的,缓存溢出的可能性就比较小。
3 多线程程序使用了线程不安全的函数。
4多线程读写的数据未加锁保护。对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump
5 随 意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的 指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时 就很容易因为bus error而core dump.
6 堆栈溢出.不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误
并不是所有的信号都能够产生core file,在signal.h的头文件中定义了哪些signal可以产生core file,如下:
2.生成core文件
其实系统会在程序崩溃的那一刹那将整个内核的信息记录在一个文件里边,(ls 并不会看到这个文件)
2.1.使用命令 ulimit -a 查看core文件大小限制
打开ulimit这个文件 发现这个文件大小缺省为0 文件根本就装不进去
那么就需要我们手动将这个文件的大小改大一点。
2.2使用 命令 ulimit -c 10240 (10240的位置可以是任意一个比较大的数字) 或者ulimit -c unlimited
可以使用命令 file core ,将core这个文件的具体信息给显示出来,命令最后会显示这个core文件是通过哪个文件产生的(假如是a.out)
PS:file命令是用来探测给定文件的类型。file命令对文件的检查分为文件系统、魔法幻数检查和语言检查3个过程。
note:更改只对当前控制台有效,而且只对该语句生效后启动的程序有效。如果想要每次启动有效,需要写入配置文档里。
/etc/profile中加入以下一行,这将允许生成coredump文件
ulimit-c unlimited
2.3 编译的时候一定要加入-g选项,要不然在最后显示错误的时候只会显示错的地址,而不会显示错误的具体信息
2.4生成core的路径
/proc/sys/kernel/core_uses_pid可以控制core文件的文件名中是否添加pid作为扩展。文件内容为1,表示添加pid作为扩展名,生成的core文件格式为core.xxxx;为0则表示生成的core文件同一命名为core。
可通过以下命令修改此文件:
echo "1" > /proc/sys/kernel/core_uses_pid
一般core路径和可执行程序一个路径。
除此之外,还可以在/proc/sys/kernel/core-pattern里设置core文件的文件名模板
例如
echo /data/coredump/core.%e.%p> /proc/sys/kernel/core_pattern
将更改core文件生成路径,自动放在这个/data/coredump文件夹里。
%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加当前uid
%g - insert current gid into filename 添加当前gid
%s - insert signal that caused the coredump into the filename 添加导致产生core的信号
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加程序名
3.调试core
3.1 基本命令
gdb 源程序 core文件
3.2附:GDB调试基本命令
-
list/l 命令
可以使用list/l命令查看程序,方便我们添加断点时查看信息。
list+lineNumber(中间有空格)
list 打印函数名称为Function的函数上下文的源程序
list 输出当前行后面的代码
list -显示当前行前面的代码 -
run/r命令
在gdb中运行程序使用run命令.也可以设置程序运行参数。pwd命令用于显示当前所在目录。 -
break/b命令
break < function >
在进入指定的函数function时既停止运行,C++中可以使用class::function或function(type, type)格式来指定函数名称
break < lineNumber>
在指定的代码行打断点
break +offset/break -offset
在当前行的前面或后面的offset行打断点,offset为自然数
break filename:lineNumber
在名称为filename的文件中的第lineNumber行打断点
break filename:function
在名称为filename的文件中的function函数入口处打断点
break *address
在程序运行的内存地址处打断点
break
在下一条命令处停止运行
break … if < condition>
在处理某些循环体中可使用此方法进行调试,其中…可以是上述的break lineNumber、break +offset/break -offset中的参数,其中condition表示条件,在条件成立时程序即停止运行,如设置break if i=100表示当i为100时程序停止运行。
查看断点时,也可以使用info命令如info breakpoints [n]、info break [n]其中n 表示断点号来查看断点信息。 -
逐步调试命令
next < count>。单步跟踪,如果有函数调用不会进入函数,如果后面不加count表示一条一条的执行,加count表示执行后面的count条指令,
step < count>。单步跟踪,如果有函数调用则进入该函数(进入该函数前提是此函数编译有Debug信息),与next类似,其不加count表示一条一条执行,加上count表示自当前行开始执行count条代码指令
set step-mode.set step-mode on用于打开step-mode模式,这样在进行单步跟踪时,程序不会因为没有debug信息而不停止运行,这很有利于查看机器码,可以通过set step-mode off关闭step-mode模式
finish。运行程序直到当前函数完成并打印函数返回时的堆栈地址和返回值及参数值等信息。
until。运行程序直到退出循环体
stepi(缩写si)和nexti(缩写ni)。stepi和nexti用于单步跟踪一条及其指令,一条程序代码有可能由数条机器指令完成,stepi和nexi可以单步执行机器指令。 -
continue/c命令
当程序遇到断点停止运行后可以使用continue命令恢复程序的运行到下一个断点或直到程序结束。 -
print命令
请查看:https://blog.csdn.net/linuxheik/article/details/17380767 -
watch命令
watch命令一般来观察某个表达式(变量也可视为一种表达式)的值是否发生了变化,如果由变化则程序立即停止运行,其具体用法如下:
watch < expr>
为表达式(变量)expr设置一个观察点一旦其数值由变化,程序立即停止运行
rwatch < expr>
当表达式expr被读时,程序立即停止运行
awatch < expr>
当表达式expr的值被读或被写时程序立即停止运行
info watchpoints
列出当前所设置的所有观察点
8. return命令
如果在函数中设置了调试断点,在断点后还有语句没有执行完,这个时候我们可以使用return命令强制函数忽略还没有执行的语句并返回。可以直接使用return命令用于取消当前函数的执行并立即返回函数值,也可以指定表达式如 return < expression>那么该表达式的值会被作为函数的返回值。
9. info命令
info命令可以用来在调试时查看寄存器、断点、观察点和信号等信息。其用法如下:
info registers:查看除了浮点寄存器以外的寄存器
info all-registers: 查看所有的寄存器包括浮点寄存器
info registers < registersName>:查看指定寄存器
info break: 查看所有断点信息
info watchpoints: 查看当前设置的所有观察点
info signals info handle: 查看有哪些信号正在被gdb检测
info line: 查看源代码在内存中的地址
info threads: 可以查看多线程
10. finish命令
执行完当前的函数。
run(缩写r)和quit(缩写q)分别可以开始运行程序和退出gdb调试
whatis或ptype显示变量的类型
bt显示函数调用路径
Good Lock!!!!
更多推荐
所有评论(0)