嵌入式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、野指针与段错误问题1.1、什么是野指针?所谓野指针就是,指针指向一个不确定的地址空间,或者指向的是一个确定的地址空间的,但引用空间的结果却是不可预知的,这样的指针就称为野指针。
例子1:int main(void) {int *p;*p = 10;return 0;}本例子中,p是自动局部变量,由于p没有被初始化,也没有被后续赋值,那么p中存放的是一个随机值,所以p指向的内存空间是不确定的,访问一个不确定的地址空间,结果显然是不可预知的。
例子1:int main(void) {int *p=0x13542354;*p = 10;return 0;}本例子中,p虽然是指向了一个确定地址“0x43542354”的空间,但是它对应的空间是否存在,其读写权限是否满足程序的访问要求,都是未知数,所以导致的结果也是未知的。
通过以上两个例子了解到,在给指针变量绑定地址,指向某个空间时,一定要是确定的,不能出现不可预知性,一旦出现未知性它就是一个野指针,即使某一次没有产生严重后果,但埋下了这颗地雷后,就留下了不可预知的隐患,对于程序来说这是不可接受的。
1.2、野指针可能引发的危害(1)引发段错误段错误就是地址错误,其实是一种对程序和系统的保护性措施,一旦产生段错误,程序会立即终止,防止错误循环叠加,产生雪崩式的错误。
(2)未产生任何结果有的时候,可能使用了一个野指针,虽然指向了一个未知的地址空间,但是这空间可以使用,而且该空间和程序中的其它变量空间没有交集,对野指针指向的空间进行了读写访问后,也不会对程序产生任何影响,虽然如此,这种野指针也是必须要极力避免的,因为这个隐患很可能在后面的程序运行中,导致严重的错误,而且这种错误很难排查。
(3)引发程序连环式错误访问野指针产生的错误循环叠加,轻者程序结果严重扭曲,重者直接导致程序或者系统崩溃。
1.3、野指针产生的原因野指针产生的原因大概有如下三种:(1)在使用指针前,忘记了给指针变量初始化或者赋值一个有效的空间地址,导致指向的不确定。
常见内存错误
随着诸如代码重构和单元测试等方法引入实践,调试技能渐渐弱化了,甚至有人主张废除调试器。
这是有道理的,原因在于调试的代价往往太大了,特别是调试系统集成之后的BUG,一个BUG花了几天甚至数周时间并非罕见。
而这些难以定位的BUG基本上可以归为两类:内存错误和并发问题。
而又以内存错误最为普遍,即使是久经沙场的老手,也有时也难免落入陷阱。
前事不忘,后世之师,了解这些常见的错误,在编程时就加以注意,把出错的概率降到最低,可以节省不少时间。
这些列举一些常见的内存错误,供新手参考。
1. 内存泄露。
大家都知道,在堆上分配的内存,如果不再使用了,应该把它释放掉,以便后面其它地方可以重用。
在C/C++中,内存管理器不会帮你自动回收不再使用的内存。
如果你忘了释放不再使用的内存,这些内存就不能被重用,就造成了所谓的内存泄露。
把内存泄露列为首位,倒并不是因为它有多么严重的后果,而因为它是最为常见的一类错误。
一两处内存泄露通常不至于让程序崩溃,也不会出现逻辑上的错误,加上进程退出时,系统会自动释放该进程所有相关的内存,所以内存泄露的后果相对来说还是比较温和的。
当然了,量变会产生质变,一旦内存泄露过多以致于耗尽内存,后续内存分配将会失败,程序可能因此而崩溃。
现在的PC机内存够大了,加上进程有独立的内存空间,对于一些小程序来说,内存泄露已经不是太大的威胁。
但对于大型软件,特别是长时间运行的软件,或者嵌入式系统来说,内存泄露仍然是致命的因素之一。
不管在什么情况下,采取比较谨慎的态度,杜绝内存泄露的出现,都是可取的。
相反,认为内存有的是,对内存泄露放任自流都不是负责的。
尽管一些工具可以帮助我们检查内存泄露问题,我认为还是应该在编程时就仔细一点,及早排除这类错误,工具只是用作验证的手段。
2.内存越界访问。
内存越界访问有两种:一种是读越界,即读了不属于自己的数据,如果所读的内存地址是无效的,程度立刻就崩溃了。
如果所读内存地址是有效的,在读的时候不会出问题,但由于读到的数据是随机的,它会产生不可预料的后果。
在Linux上什么是段错误?如何获得一个核心转储-
在Linux上什么是段错误?如何获得一个核心转储?本周工作中,我花了整整一周的时间来尝试调试一个段错误。
我以前从来没有这样做过,我花了很长时间才弄清楚其中涉及的一些基本事情(获得核心转储、找到导致段错误的行号)。
于是便有了这篇博客来解释如何做那些事情!在看完这篇博客后,你应该知道如何从“哦,我的程序出现段错误,但我不知道正在发生什么”到“我知道它出现段错误时的堆栈、行号了!”。
什么是段错误?“段错误segmentation fault”是指你的程序尝试访问不允许访问的内存地址的情况。
这可能是由于:试图解引用空指针(你不被允许访问内存地址 0);试图解引用其他一些不在你内存(LCTT 译注:指不在合法的内存地址区间内)中的指针;一个已被破坏并且指向错误的地方的C++++ 虚表指针C++ vtable pointer,这导致程序尝试执行没有执行权限的内存中的指令;其他一些我不明白的事情,比如我认为访问未对齐的内存地址也可能会导致段错误(LCTT 译注:在要求自然边界对齐的体系结构,如MIPS、ARM 中更容易因非对齐访问产生段错误)。
这个“C++ 虚表指针”是我的程序发生段错误的情况。
我可能会在未来的博客中解释这个,因为我最初并不知道任何关于C++ 的知识,并且这种虚表查找导致程序段错误的情况也是我所不了解的。
但是!这篇博客后不是关于C++ 问题的。
让我们谈论的基本的东西,比如,我们如何得到一个核心转储?运行valgrind我发现找出为什么我的程序出现段错误的最简单的方式是使用 valgrind:我运行valgrind -vyour-program这给了我一个故障时的堆栈调用序列。
简洁!。
字节序_嵌入式Linux C语言程序设计基础教程(微课版)_[共2页]
嵌入式Linux C语言程序设计基础教程 (微课版)56 的处理器字长为32位。
为了解决不同的体系结构有不同字长的问题,嵌入式Linux中给出两种数据类型,其一是不透明数据类型,其二是长度明确的数据类型。
不透明数据类型隐藏了它们的内部格式或结构。
在C语言中,它们就像黑盒一样,开发者们利用typedef 声明一个类型,把它叫作不透明数据类型,并希望其他开发者不要重新将其转化为对应的那个标准C语言类型。
例如用来保存进程标识符的pid_t类型的实际长度就被隐藏起来了,尽管任何人都可以揭开它的面纱,因为其实它就是一个int型数据。
长度明确的数据类型也很常见。
作为一个程序员,通常在程序中需要操作硬件设备,这时就必须明确知道数据的长度。
嵌入式Linux内核在“asm/types.h”中定义了这些长度明确的类型,表2-10是这些类型的完整说明。
表2-10 类型说明类型 描述 类型 描述s8 带符号字节 s32 带符号32位整数u8 无符号字节 u32 无符号32位整数s16 带符号16位整数 s64 带符号64位整数u16 无符号16位整数 u64 无符号64位整数这些长度明确的数据类型大部分是通过typedef对标准的C语言类型进行映射得到的。
在嵌入式Linux中的“/asm-arm/types.h”就有如下定义。
typedef __signed__ char __s8;typedef unsigned char __u8;typedef __signed__ short __s16;typedef unsigned short __u16;typedef __signed__ int __s32;typedef unsigned int __u32;typedef __signed__ long long __s64;typedef unsigned long long __u64;2.5.2 数据对齐对齐是内存数据与内存中的相对位置相关的问题。
嵌入式开发资料_Linux系统错误速查手册
33
EDOM
Math argument out of domain of func
esult not representable
35
EDEADLK
Resource deadlock would occur
EDEADLOCK
36
ENAMETOOLONG
CMS_SUBSYSTEM
CMS subsystem
D
DATA_MGMT_SUBSYSTEM
Data Management subsystem
E
MEMORY_ADMIN_SUBSYSTEM
Memory Management subsystem
F
FRAMEWORK_SVCS_SUBSYSTEM
Framework Service subsystem
I
IPT_SUBSYSTEM
IP Tunnel subsystem
KITS_SUBSYSTEM
Kits subsystem
K
KERNEL_SUBSYSTEM
Kernel subsystem
N
NETUTIL_SUBSYSTEM
Net utility subsystem
O
OM_SUBSYSTEM
Object Management subsystem
77
EBADFD
File descriptor in bad state
78
EREMCHG
Remote address changed
79
ELIBACC
Can not access a needed shared library
80
ELIBBAD
嵌入式-Linux-位运算详解
嵌入式-Linux-位运算详解1、常用位操作符图2-11.1、位与(&)位与就是对数的二进制位位进行运算。
两个数每个二进制位的运算规则按照如下规则运算,该规则就是其真值表(如下)。
从其运算规则(真值表)可以看出只有1和1进行与运算的结果是1,其余的全是0,如果我们将1当做为真,0当做假的话,按照与运算的要求,两个为真才为真,只要有一个为假就为假。
好了,我们看下面一个例子:3 & 5 = ?(1)分析可知这两个是十进制数,所以先把这两个数都化为二进制数:3的二进制:0b00115的二进制:0b0101(2)将这两个数的二进制形式按照上面的运算规则进行按位与运算:0b0011 (3)& 0b0101 (5)= 0b0001 (1)(3)将得到的二进制结果0b0001变为十进制,十进制结果为1。
所以可以得出结论:3 & 5 = 1。
扩展:&(按位与)和 &&(逻辑与)的区别&&(逻辑与)是将要运算的两个数都看做成一个整体,而这个整体如果是0,则该数被定义成逻辑假(0),如果该数不为0(不管是正的还是负的)则被定义成逻辑真(1)。
来看几个小例子:(1)3 && 5 = ?分析:3(逻辑真) 5(逻辑真),真 && 真 = 真,所以结果为真,(2)3 && 0 = ?分析:3(逻辑真) 0(逻辑假),真 && 假 = 假,所以结果为假,即:3 && 0 = 0(3)3 && -5 = ?分析:3(逻辑真) -5(逻辑真),真 && 真 = 真,所以结果为真,即:3 && -5 = 11.2、位或(|)从其真值表可以看出只有0和0进行或运算的结果是0,其余的全是1,如果1为真,0为假,或运算就是,只要有一个为真就为真,两个为假才为假。
嵌入式Linux 应用开发教程 用gdb调试程序的bug
计算机工程系实验报告课程名称:嵌入式Linux 应用开发教程班级:姓名:学号:实验目的:通过调试一个有问题的程序,进一步熟练使用vi操作,而且熟练掌握gcc编译命令及gdb的调试命令,通过对有问题程序的跟踪调试,进一步提高发现问题和解决问题的能力。
实验环境:linux 编译器gcc 调试器gdb vi编辑器实验内容及过程:(1)使用vi编辑器,将一下代码输入到名为greet.c的文件中。
此代码的原意为输出倒序main函数中定义的字符串,但结果显示没有输出,代码如下所示。
#include <studio.h>int display1(char *string);int display2(char *string);int main(){char string[]= “Embedded Linux”;display1 (string);display2 (string);}Int display1 (char *string){printf (“The original string is %s \n”, string);}Int display2 (char *string1){char *string2;int size,i;size = strlen (string1);string = (char *) malloc (size + 1);for (i = 0;i < size;i++){String2[size - i] =string1[i];}String[size+1] = ‘’;printf(“The string afterward is %s\n”,string2);}(2)使用gcc编译这段代码,注意要加上“-g”选项以方便之后的调试。
(3)运行生成的可执行文件,观察运行结果。
(4)使用gdb调试程序,通过设置断点,单步跟踪,一步步找出错误所在。
(5)纠正错误,更改源程序并得到正确的结果。
野指针受害记Unix系统-电脑资料
野指针受害记Unix系统-电脑资料以前曾听高手和很多书籍谈到指针以及为此带来的方便和烦恼,。
这次,我在写一个将数据记录到文件的小程序时没想到也会碰到野指针。
程序如下: #include unis td .h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include ma 以前曾听高手和很多书籍谈到指针以及为此带来的方便和烦恼。
这次,我在写一个将数据记录到文件的小程序时没想到也会碰到野指针。
程序如下:#include <unis td.h></unis#include#include#include#include#include#includemain(){int fd;FILE *fd_stream;char buff=' ',*pp,*qq;int ss,i,j;char* getASCII(short param);if((fd=open("bb.txt",O_WRONLY|O_CREAT|O_TRUNC,S_I RWXU))==-1){printf("open() error\");exit(1);}for(j=0;j<10;j++){pp=getASCII(3845);//printf("\qq:%8d\",qq);for(i=0;i<sizeof(pp)+1;i++)< p=""></sizeof(pp)+1;i++)<> {printf("%c",*(pp+i));write(fd,(pp+i),1);}for(;i<20;i++){write(fd,&buff,1);printf(" ");}printf("a\");}i=0;qq=getASCII(9876);//printf("\qq:%8d\",qq);for(i=0;i<sizeof(qq)+1;i++)< p=""></sizeof(qq)+1;i++)<> {printf("%c",*(qq+i));write(fd,(qq+i),1);}for(;i<20;i++){write(fd,&buff,1);printf(" ");}printf("a\");}char* getASCII(short param){int temp;char* result;int ratio=10000,i;result=calloc(10,1);//allocate 10 continuous unit,1 Byte per unit---------------------------所有的奥妙就在这一句,如果不给这个指针赋值,那他就是个野指针,电脑资料《野指针受害记Unix系统》(https://www.)。
- 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)小段错误
产生的原因:指针变量指向的地址空间存在,但是对该空间的操作权限受到了限制,比如希望写,但是该空间不允许写,这也会导致段错误。