嵌入式linux课程-野指针与段错误
linux C用户态调试追踪函数调用堆栈以及定位段错误

linux C用户态调试追踪函数调用堆栈以及定位段错误一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的。
在glibc头文件"execinfo.h"中声明了三个函数用于获取当前线程的函数调用堆栈。
int backtrace(void **buffer,int size)该函数用于获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针列表。
参数size 用来指定buffer中可以保存多少个void* 元素。
函数返回值是实际获取的指针个数,最大不超过size大小在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址注意:某些编译器的优化选项对获取正确的调用堆栈有干扰,另外内联函数没有堆栈框架;删除框架指针也会导致无法正确解析堆栈内容char ** backtrace_symbols (void *const *buffer, int size)backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组. 参数buffer应该是从backtrace函数获取的指针数组,size是该数组中的元素个数(backtrace的返回值)函数返回值是一个指向字符串数组的指针,它的大小同buffer相同.每个字符串包含了一个相对于buffer中对应元素的可打印信息.它包括函数名,函数的偏移地址,和实际的返回地址现在,只有使用ELF二进制格式的程序才能获取函数名称和偏移地址.在其他系统,只有16进制的返回地址能被获取.另外,你可能需要传递相应的符号给链接器,以能支持函数名功能(比如,在使用GNU ld链接器的系统中,你需要传递(-rdynamic),-rdynamic可用来通知链接器将所有符号添加到动态符号表中,如果你的链接器支持-rdynamic的话,建议将其加上!) 该函数的返回值是通过malloc函数申请的空间,因此调用者必须使用free函数来释放指针. 注意:如果不能为字符串获取足够的空间函数的返回值将会为NULLvoid backtrace_symbols_fd (void *const *buffer, int size, int fd)。
Linux下的段错误(Segmentationfault)产生的原因及调试方法(经典)

Linux下的段错误(Segmentation fault )产生的原因及调试方法(经典)2009-04-05 11:25简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址.一般来说,段错误就是指访问的内存超出了系统所给这个程序的内存空间,通常这个值是由gdtr来保存的,他是一个48位的寄存器,其中的32位是保存由它指向的gdt表,后13位保存相应于gdt的下标,最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向的gdt是由以64位为一个单位的表,在这张表中就保存着程序运行的代码段以及数据段的起始地址以及与此相应的段限和页面交换还有程序运行级别还有内存粒度等等的信息。
一旦一个程序发生了越界访问,cpu就会产生相应的异常保护,于是segmentation fault就出现了•在编程中以下几类做法容易导致段错误,基本是是错误地使用指针引起的1)访问系统数据区,尤其是往系统保护的内存地址写数据最常见就是给一个指针以0地址2)内存越界(数组越界,变量类型不一致等)访问到不属于你的内存区域解决方法我们在用C/C++语言写程序的时侯,内存管理的绝大部分工作都是需要我们来做的。
实际上,内存管理是一个比较繁琐的工作,无论你多高明,经验多丰富,难免会在此处犯些小错误,而通常这些错误又是那么的浅显而易于消除。
但是手工除虫”(debug),往往是效率低下且让人厌烦的,本文将就”段错误”这个内存访问越界的错误谈谈如何快速定位这些"段错误”的语句。
下面将就以下的一个存在段错误的程序介绍几种调试方法:1 dummy_function (void)2 {3 unsigned char *ptr = 0x00;4 *ptr = 0x00;5 }67 int main (void)8 {9 dummy_function ();1011 return 0;12 }作为一个熟练的C/C++程序员,以上代码的bug应该是很清楚的,因为它尝试操作地址为0 的内存区域,而这个内存区域通常是不可访问的禁区,当然就会出错了。
Linux 的异常处理

.endm
name:要生成的 vector_xxx 中的 xxx,比如 vector_stub irq, IRQ_MODE, 4,生成 vector_irq, 即 Exception 向量中 IRQ 对应的跳转地址。
mode:设定 CPU 模式位(CPSR 的 M[4:0]),从而可以操作对应模式的特殊寄存器。
图中显示,所有 7 种类型的 Exception 的跳转地址按顺序从基地址开始排列,被称
为 Exception 向 量 。 ARMv6 支 持 两 种 基 地 址 ( Normal:0x00000000,High vector:0xFFFF0000),具体使用哪个基地址,由 ARMv6 内部的 Control Register 的 bit13(V bit)设定。Linux 使用 High vector,需要注意的是,Exception 向量地址也是 虚拟地址(如果使用了虚拟地址机制)。
bl trace_hardirqs_off
#endif
get_thread_info tsk
#ifdef CONFIG_PREEMPT
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
add r7, r8, #1
一种嵌入式系统内存越界错误快速定位方法

说明书摘要发明名称一种嵌入式系统内存越界错误快速定位方法申请人武汉烽火网络有限责任公司地址430074 湖北省武汉市发明人戴锦友, 余少华,汪学舜,刘志炉摘要本发明涉及一种嵌入式系统中的内存越界错误快速定位方法,具体涉及一种借助于可编程逻辑器件,结合硬件和软件,快速确定引起内存越界错误的任务/进程以及该任务/进程调用的直接引起内存越界错误的具体函数。
该方法的目标是:通过可编程器件监视被越界使用的内存区域,当该内存区域被访问时,可编程逻辑器件向CPU产生中断,中断服务程序查询在发生本次中断前,当前正在执行的任务A,并借助于可编程逻辑器件上存储的任务信息,判断任务A是否是该内存区域的合法访问任务,如果不是,则将任务A的信息告知检测任务/进程M,任务/进程M通过任务A的控制块得到当前该任务执行的指令计数器,基于指令计数器的值得到访问该内存区域的函数,如果该函数是该内存区域合法的访问函数,则系统恢复继续运行,否则确定该函数是直接引起内存越界的函数。
摘 要 附 图A. 可编程逻辑器件的功能B .CPU 的功能 B.1 中断服务程序的功能B.2 管理任务/进程的功能权利要求书1. 一种嵌入式系统内存越界的快速检测方法,包括如下特征:(1)嵌入式系统使用一片可编程逻辑器件,将嵌入式系统内CPU 的地址总线作为可编程逻辑器件的输入,将可编程逻辑器件的1路信号设计为输出,并将它与CPU的外部中断请求信号相连接。
(2)通过CPU设置可编程器件的内存监视范围M和可以合法访问该内存区域的任务信息及函数信息。
(3)当嵌入式系统正常工作时,可编程器件周期性的对地址总线采样,如果当前采样的地址在监视的内存范围内,则可编程器件通过输出信号向CPU发出中断请求。
(4)系统软件中,有一个中断服务程序S对应上述的中断,同时有一个检测任务/进程C用于对中断服务程序S得到的信息作进一步处理。
(5)CPU收到中断请求时,执行中断服务程序,中断服务程序获取当前执行的任务/进程A,并基于可编程逻辑器件存储的任务信息判断任务/进程A是否为监视的内存区域M的合法访问任务,如是,则中断处理结束;如不是,则向任务/进程C通告任务/进程A的相关信息。
Linux学习-常见错误和快捷操作

Linux下命令的一些异常情况命令不全:在命令没有输入完 (引号或括号没有配对),就不小心按下了Enter 键,终端会提示出一个>代表命令不完整,这是可以继续输入,也可以ctrl+c 终止输入,重新再来。
(下面sed命令使用时,还有另外一种命令不全的问题)ct@ehbio:~/ehbio_project$ rename 'ehbio2 >'ct@ehbio:~/ehbio_project$ rename 'ehbio2 >^Cct@ehbio:~/ehbio_project$文件名输入错误: 多一个字母、少一个字母、大小写问题ct@ehbio:~/ehbio_project$lsehbio2.fa ehbio3.fa ehbio4.fa ehbio.fa second.fa# 重命名没有生效ct@ehbio:~/ehbio_project$ rename 'ehbio2''ehbio5' ebio2.fact@ehbio:~/ehbio_project$ lsehbio2.fa ehbio3.fa ehbio4.fa ehbio.fa second.fa# 仔细看是ehbio2.fa写成了ebio2.fa,更正后即可。
Z8vb3e9jtel4m99ss6e7eZ:~/ehbio_project$ rename 'ehbio2''ehbio5' ehbio2.fact@ehbio:~/ehbio_project$ lsehbio3.fa ehbio4.fa ehbio5.fa ehbio.fa second.fa所在目录不对: 访问的文件不存在于当前目录,而又没有提供绝对路径, 或软连接失效ct@ehbio:~/ehbio_project$ lsehbio3.fa ehbio4.fa ehbio5.fa ehbio6.fa ehbio.fa second .fact@ehbio:~/ehbio_project$ ls ../dataehbio2.fa first.fa# 当前目录没有ehbio2.fact@ehbio:~/ehbio_project$ less ehbio2.faehbio2.fa: 没有那个文件或目录# ehbio2.fa在上一层目录的data目录下ct@ehbio:~/ehbio_project$ ls ../data/ehbio2.fa../data/ehbio2.fa# 加上路径依然访问不了ct@ehbio:~/ehbio_project$ less ../data/ehbio2.fa../data/ehbio2.fa: 没有那个文件或目录# 上面的问题是软连接失效,在之前的操作中删掉了原始的ehbio2.fa,所以快捷方式失效# 正确的访问ct@ehbio:~/ehbio_project$ tail -n 3 ../data/first.fa ACGGAGCGAGCTAGTGCAGCGAGGAGCTGAGTCGAGCCAGGACAGGAGCTAendLinux终端常用快捷操作•命令或文件名自动补全:在输入命令或文件名的前几个字母后,按Tab键,系统会自动补全或提示补全•上下箭头:使用上下箭头可以回溯之前的命令,增加命令的重用,减少输入工作量•!加之前输入过的命令的前几个字母,快速获取前面的命令ct@ehbio:~/ehbio_project$ cut -f 1-d ' 'ehbio.fa | tail -n 4 >mYCACGGAGCGAGCTAGTGCAGCGAGGAGCTGAGTCGAGCCAGGACAGGAGCTAendct@ehbio:~/ehbio_project$ man cut # 直接跳到上面运行的cut命令,再执行一次ct@ehbio:~/ehbio_project$ !cut cut -f 1-d ' 'ehbio.fa | tail -n 4 >mYCACGGAGCGAGCTAGTGCAGCGAGGAGCTGAGTCGAGCCAGGACAGGAGCTAend•ctrl+a回到命令的行首,用于修改常命令或注释掉命令# 写完下面的命令,突然不想运行了,又不想一个个删掉ct@ehbio:~/ehbio_project$ cut -f 1-d ' 'ehbio.fa | tail -n 4# 按ctrl+a, 回到行首,再输入`#`号,回车,命令即被注释掉。
了解嵌入式Linux Kernel错误跟踪技术才能避免出现错误

了解嵌入式Linux Kernel错误跟踪技术才能避免出现错误随着嵌入式Linux系统的广泛应用,对系统的可靠性提出了更高的要求,尤其是涉及到生命财产等重要领域,要求系统达到安全完整性等级3级以上[1],故障率(每小时出现危险故障的可能性)为10-7以下,相当于系统的平均故障间隔时间(MTBF)至少要达到1141年以上,因此提高系统可靠性已成为一项艰巨的任务。
对某公司在工业领域14 878个控制器系统的应用调查表明,从2004年初到2007年9月底,随着硬软件的不断改进,根据错误报告统计的故障率已降低到2004年的五分之一以下,但查找错误的时间却增加到原来的3倍以上。
这种解决问题所需时间呈上升的趋势固然有软件问题,但缺乏必要的手段以辅助解决问题才是主要的原因。
通过对故障的统计跟踪发现,难以解决的软件错误和从发现到解决耗时较长的软件错误都集中在操作系统的核心部分,这其中又有很大比例集中在驱动程序部分[2]。
因此,错误跟踪技术被看成是提高系统安全完整性等级的一个重要措施[1],大多数现代操作系统均为发展提供了操作系统内核“崩溃转储”机制,即在软件系统宕机时,将内存内容保存到磁盘[3],或者通过网络发送到故障服务器[3],或者直接启动内核调试器[4]等,以供事后分析改进。
基于Linux操作系统内核的崩溃转储机制近年来有以下几种:(1) LKCD(Linux Kernel Crash Dump)机制[3];(2) KDUMP(Linux Kernel Dump)机制[4];(3) KDB机制[5];(4) KGDB机制[6]。
综合上述几种机制可以发现,这四种机制之间有以下三个共同点:(1) 适用于为运算资源丰富、存储空间充足的应用场合;(2) 发生系统崩溃后恢复时间无严格要求;(3) 主要针对较通用的硬件平台,如X86平台。
在嵌入式应用场合想要直接使用上列机制中的某一种,却遇到以下三个难点无法解决:。
嵌入式linux内核编译错误的一些解决办法

make[2]:***[drivers/video/console2] error 2
make[1]:***[drivers/video1] error 2
make:***[drivers] error 2
解决方法:
在make menuconfig 时选哪个设备驱动的选项进去在选Graphics support ->
scripts/basic/docproc.c: 在函数‘parse_file’中:
scripts/basic/docproc.c:296: 警告: 对指针赋值时目标与指针符号不一致
SHIPPED scripts/kconfig/zconf.tab.h
HOSTCC scripts/kconfig/conf.o
四:
make zImage和make xipImage
Kernel configured for XIP (CONFIG_XIP_KERNEL=y)
Only the xipImage target is available in this case
make[1]: *** [arch/arm/boot/zImage] Error 1
make: *** [.tmp_vmlinux1] 错误 1
ld链接时产生错误
对应行:
/home/kevin/ARMSystem/linux-2.6.12/arch/arm/kernel/vmlinux.lds
/* those must never be empty */
ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")
linux下编程遇到的一个疑难杂症——野指针

linux下编程遇到的一个疑难杂症——野指针所带来的害处分类:LINUX网络编程2012-04-12 14:15 653人阅读评论(0) 收藏举报场景:一段代码,单进程多线程模式,除了main线程还有很多个子线程,里面大量地使用了指针,代码编译没有任何的warning和error;程序跑起来后一切正常,并和另外一个程序通过socket 网络通信,也一切正常;所有的子线程退出也正常;所有类的析构函数析构的时候也正常。
但是:就在main进程销毁(或者通过exit(0))退出的时候,操作系统就提示以下错误:*** glibc detected *** ./fsvspsiu: corrupted double-linked list: 0x08484100 ***======= Backtrace: =========/lib/libc.so.6[0x3bce1b]/lib/libc.so.6[0x3be7fb]/lib/libc.so.6(cfree+0x90)[0x3c20f0]./fsvspsiu[0x804d4c5]./fsvspsiu[0x804c411]./fsvspsiu[0x804a134]/lib/libc.so.6(exit+0xee)[0x38163e]/lib/libc.so.6(__libc_start_main+0xe8)[0x36b398]./fsvspsiu(__gxx_personality_v0+0x69)[0x8049211]======= Memory map: ========00110000-00111000 r-xp 00110000 00:00 0 [vdso]00336000-00351000 r-xp 00000000 fd:00 332652 /lib/ld-2.7.so00351000-00352000 r-xp 0001a000 fd:00 332652 /lib/ld-2.7.so00352000-00353000 rwxp 0001b000 fd:00 332652 /lib/ld-2.7.so00355000-004a8000 r-xp 00000000 fd:00 332653 /lib/libc-2.7.so004a8000-004aa000 r-xp 00153000 fd:00 332653 /lib/libc-2.7.so004aa000-004ab000 rwxp 00155000 fd:00 332653 /lib/libc-2.7.so004ab000-004ae000 rwxp 004ab000 00:00 0004b0000-004d7000 r-xp 00000000 fd:00 332657 /lib/libm-2.7.so004d7000-004d8000 r-xp 00026000 fd:00 332657 /lib/libm-2.7.so004d8000-004d9000 rwxp 00027000 fd:00 332657 /lib/libm-2.7.so004e2000-004f7000 r-xp 00000000 fd:00 332655 /lib/libpthread-2.7.so004f7000-004f8000 r-xp 00014000 fd:00 332655 /lib/libpthread-2.7.so004f8000-004f9000 rwxp 00015000 fd:00 332655 /lib/libpthread-2.7.so004f9000-004fb000 rwxp 004f9000 00:00 000cab000-00cb6000 r-xp 00000000 fd:00 332676 /lib/libgcc_s-4.1.2-20070925.so.100cb6000-00cb7000 rwxp 0000a000 fd:00 332676 /lib/libgcc_s-4.1.2-20070925.so.103694000-03774000 r-xp 00000000 fd:00 415472 /usr/lib/libstdc++.so.6.0.803774000-03778000 r-xp 000df000 fd:00 415472 /usr/lib/libstdc++.so.6.0.803778000-03779000 rwxp 000e3000 fd:00 415472 /usr/lib/libstdc++.so.6.0.803779000-0377f000 rwxp 03779000 00:00 008048000-08052000 r-xp 00000000 00:14 85112 /mnt/hgfs/d/fsvsp/src/pf/siu/fsvspsiu08052000-08053000 rw-p 00009000 00:14 85112 /mnt/hgfs/d/fsvsp/src/pf/siu/fsvspsiu08053000-08057000 rw-p 08053000 00:00 008452000-08486000 rw-p 08452000 00:00 0b5600000-b5621000 rw-p b5600000 00:00 0b5621000-b5700000 ---p b5621000 00:00 0b5718000-b5719000 ---p b5718000 00:00 0b5719000-b6119000 rw-p b5719000 00:00 0b6119000-b611a000 ---p b6119000 00:00 0b611a000-b6b1a000 rw-p b611a000 00:00 0b6b1a000-b6b1b000 ---p b6b1a000 00:00 0b6b1b000-b751b000 rw-p b6b1b000 00:00 0b751b000-b751c000 ---p b751b000 00:00 0b751c000-b7f1f000 rw-p b751c000 00:00 0bfe86000-bfe9b000 rw-p bffea000 00:00 0 [stack]Aborted1. 通过一翻周折,终于找到了问题的根源:看上面操作系统提示信息的第五行( /lib/libc.so.6(cfree+0x90)[0x3c20f0] ),可以估计应该是堆内存被free的时候出了问题,那么free()函数最有可能会出现问题的情况是什么呢——堆被多次释放了。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
野指针与段错误
1、野指针与段错误问题
1.1、什么是野指针?
所谓野指针就是,指针指向一个不确定的地址空间,或者指向的是一个确定的地址空间的,但引用空间的结果却是不可预知的,这样的指针就称为野指针。
例子1:
int main(void){
int*p;
*p=10;
return0;
}
本例子中,p是自动局部变量,由于p没有被初始化,也没有被后续赋值,那么p中存放的是一个随机值,所以p指向的内存空间是不确定的,访问一个不确定的地址空间,结果显然是不可预知的。
例子1:
int main(void){
int*p=0x13542354;
*p=10;
return0;
}
本例子中,p虽然是指向了一个确定地址“0x43542354”的空间,但是它对应的空间是否存在,其读写权限是否满足程序的访问要求,都是未知数,所以导致的结果也是未知的。
通过以上两个例子了解到,在给指针变量绑定地址,指向某个空间时,一定要是确定的,不能出现不可预知性,一旦出现未知性它就是一个野指针,即使某一次没有产生严重后果,但埋下了这颗地雷后,就留下了不可预知的隐患,对于程序来说这是不可接受的。
1.2、野指针可能引发的危害
(1)引发段错误
段错误就是地址错误,其实是一种对程序和系统的保护性措施,一旦产生段错误,程序会立即终止,防止错误循环叠加,产生雪崩式的错误。
(2)未产生任何结果
有的时候,可能使用了一个野指针,虽然指向了一个未知的地址空间,但是这空间可以使用,而且该空间和程序中的其它变量空间没有交集,对野指针指向的空间进行了读写访问后,也不会对程序产生任何影响,虽然如此,这种野指针也是必须要极力避免的,因为这个隐患很可能在后面的程序运行中,导致严重的错误,而且这种错误很难排查。
(3)引发程序连环式错误
访问野指针产生的错误循环叠加,轻者程序结果严重扭曲,重者直接导致程序或者系统崩溃。
1.3、野指针产生的原因
野指针产生的原因大概有如下三种:
(1)在使用指针前,忘记了给指针变量初始化或者赋值一个有效的空间地址,导致指向的不确定。
(2)不清楚某些地址空间的访问权限,但是指针试图指向这些空间,并且按照不允许的权限去操作。
例如:int*p=“hello”*(p+1)=‘w’;
由于hello作为字符串常量,被存放在了内存中的常量节中,该段内存只允许读操作,但是该例子却想将’e’修改’w’,试图写不允许写的空间,一定会导致段错误。
(3)访问空间时,内存越位导致野指针,比如:
int buf[4]={0};
*(buf+4)=10;
数组只有0、1、2、3这几个成员,但是例子中却试图访问超出数组范围的空间,导致的结果要么段错误,要么程序结果不对,要么虽然没有明显错误,但是留下一个隐患导致程序崩溃。
1.4、如何避免野指针
避免野指针的方法大致有如下4点:
(1)养成良好习惯,定义指针变量时,将其赋值为NULL,如果说真的访问到NULL了,起码直接导致段错误,立即终止了程序的运行,避免了更加严重的错误,而且这种情况导致的段错误也好排查。
(2)在使用指针变量前,一定要对指针变量初始化或赋值,让它指向一个有效且确定的空间。
(3)检查指针的有效性,在使用指针前,做一下指针是否为空的判断,你如:Int*p=NULL;
If(NULL!=p){
*p=100;
}else printf(“p==NULL\n”)
对于类似底层的驱动程序而言,在使用某个指针之前,先做指针是否为空的判断是很有必要的,因为绝大多数的野指针,可能都是由于我们忘记了初始化或者赋值导致的,这样的判断可以及时提醒我们有没有初始化或者赋值,通过这种方式可以在很大程度上避免野指针的情况。
但是另一种情况就很难预防了,比如我们给指针变量初始化或者赋值时,自己给错地址了,导致指向的空间无效,面对这种情况时,程序员自己需要多加小心了。
(4)当我们确定某个指针变量不再使用时,我们就将其赋值NULL,这么做的目的就是,防止继续指向不再需要的空间,否者可能会导致内存空间的值被篡改。
但是如果严格准守以上形式的话,代码写的很累,对于代码量少且程序员本身经验又很丰富的时候,可以不用按照上面的步骤来做,但是如果本身经验不足且项目较大时,我们还是尽量按照上面的步骤来做。
1.5、NULL到底是什么
在c/c++中,NULL是这么定义的:
#ifdef_cplusplus//如果这个符号定义了,表示当前运行的是c++环境,否者
就是c环境
#define NULL0//在c++中NULL被定义为了0
#else
#define NULL(void*)0//在c中NULL被定义为了(void*)类型的0
#endif
NULL在c++和c中,会被定义成不同的形式,是因为在c++中NULL指针可以表示为整形数字0,不会做严格的类型检查,但是在c中不行,会做严格的类型检查。
1.6、段错误产生的原因汇总
(1)什么是段错误(Segmentation fault)
段错误本质上指的就是指针错误(地址错误),之所以称为段错误,大概是因为c的内存结构是由不同的内存段组成的,所以指针错误又被称为了段错误。
(2)段错误产生的几种原因
段错误其实分为了两种,一种是大段错误,另一种是小段错误。
(a)大段错误
产生的原因:指针变量指向的地址空间根本不存在而导致的。
(b)小段错误
产生的原因:指针变量指向的地址空间存在,但是对该空间的操作权限受到了限制,比如希望写,但是该空间不允许写,这也会导致段错误。