如何辨一辨 找出错误发生SEGV内存错误的程序

[环境污染产生的原因]骗子产生的原因和环境——读《大骗子克鲁尔的自白》
· · · ·
您当前的位置: → [环境污染产生的原因]骗子产生的原因和环境——读《大骗子克鲁尔的自白》
[环境污染产生的原因]骗子产生的原因和环境——读《大骗子克鲁尔的自白》
篇一 : 骗子产生的原因和环境——读《大骗子克鲁尔的自白》《大骗子克鲁尔的自白》是德国现实主义伟大作家、诺贝尔文学奖获得者托马斯.曼(1875-1955)留给后世的最后一部长篇小说,发表[]于逝世前一年,也是他唯一一部犯罪小说。《大骗子克鲁尔的自白》,顾名思义,主人公是1个大骗子。他经过监牢的“感化”,对人生有了“新的”认识之后,开始回顾自己所走过的道路。凡是1个大的欺骗行为能够取得成功,必须符合2个基本条件:一是骗子本身具有行骗的“本领”;二是社会上存在着上当受骗的客观条件,存在着容易甚至愿意上当受骗的人。这2个条件结合在一起,于是一幕幕让人无法相信的骗局也就变得顺理成章了。克鲁尔作为骗子是具有这种本领的,他自幼具有表演的天赋,装腔作势,能言善辩,随着年岁的增长和与生俱来的英俊潇洒外貌,颇有女人缘,在上层社会也左右逢源。然而,他并不是1个天生的坏蛋,而是在爸爸破产自杀和家境一落千丈,经历了种种波折之后,才1步1步学坏的。他自暴自弃,追求感官享受,玩世不恭,最后陷入欺世盗名的罪恶深渊。作者本来想把克鲁尔写成1个充满矛盾,尖锐对立的社会的产物与牺牲品,但后来在完成这部作品的过程中,作者显而易见地转向了对社会的揭露和讽刺,而对主人公克鲁尔,作者是怀着同情心来刻画的,从这部作品产生的漫长过程,我们就能看出作者是很看重这一部作品的。早在1909年,托马斯就开始了《大骗子克鲁尔的自白》的创作,经过几年的间歇于1912年又继续写作,但实际上只完成了主人公的童年时期就搁笔了。直到作者晚年(1951年)用了一年多时间才将这部作品完成。在历时四十余年的过程中,作者对社会的认识越来越深化,这部小说的主题思想也发生了变化。在这部作品中,作者以主人公自述形式,描述了1个青年走上犯罪和堕落的过程,深刻揭示了社会上出现的腐朽没落现象以及人与人之间虚伪狡诈的关系,展现出一幅极为丰富多采、生动的社会画面。克鲁尔从1个天真无邪的孩子逐渐学坏堕落,这固然有其主观原因,但是更应看到克鲁尔堕落的主要原因是社会环境造成的。爸爸的破产,使他深深体会到世态炎凉,有钱有势横行天下,而无钱无势则寸步难行。不甘心忍受贫穷的克鲁尔决心改变自己的这种境遇。他观察到区别主人与奴仆的不是人本身的价值,而是出身与门第,人的角色是可以变换的,其唯一的条件是金钱。只要有钱购置一身华丽礼服穿在身上,出入高级饭店和剧院,同样被人尊为“贵人”、“绅士”,他就用变卖偷来首饰的钱购置了华丽的礼服穿在身上,固然比那些真正的大人先生更有气魄,更有风度。社会是1个大染缸,人们都说“贫穷不是耻辱”,但只不过是说说而已,在那些阔绰富有的人看来,贫穷是极其可怕的,一半是污点一半是一般性的斥责。总而言之,是非常令人厌恶的。如果不是冒名顶替侯爵的身份去周游世界,克鲁尔会在上层社会中处处受人欢迎吗?另一方面,我们还看到在小说中克鲁尔除了在儿童时代到食品店偷过几次糖果外,从狭义的法制概念来看,实际上没有犯下任何“罪行”,例如在两性关系上,从十六岁时同年长于他十多岁的保姆,后来同一位匈牙利妓女、资本家的阔太太直到同里斯本自然博物馆馆长的夫人,无一不是在对方的主动或诱惑下发生的;又如,他“偷”资本家的阔太太的首饰,(事实上,是下车时,那位列车检查员在检查行李时,错放在他行李中的。)后来也是由对方作为“礼物”赠给了他;最后,冒名顶替路易.威诺斯塔去周游世界,以欺骗侯爵夫妇,更是对方提出的主意,并执意要他帮忙去干的。在克鲁尔看来,他们都是自愿上当受骗的,读者也基本同意他的这种看法。从这个角度看来,说他是骗子,也有点冤枉。假如克鲁尔的身份真的是侯爵出身,那么他看来所做的一切,又有谁能认为有什么不妥呢,是不符合他这个身份的人应该做的呢?因此这部作品比起任何一部作品对社会的揭露尖锐和深刻得多。读《大骗子克鲁尔的自白》,使我联想到最近1个叫赵锡永的人冒充国务院研究室司长在云南行骗,被当地政府和企业奉为上宾,并被云内动力股份有限公司聘为高级顾问。国务院研究室得知消息后,致函云南,称国务院研究室没有赵锡永这个人。令人大惑不解的是,这位假冒的司长竟然比真司长还像司长,而且清正廉洁、高风亮节,对云南地方政府和企业并非提出过分要求。按“云内动力”的人士介绍,赵锡永称,自己作为领导,只想在退休前干点实实在在的事情,不想出名。据接待赵锡永的人士表示,赵锡永不但相貌堂堂,而且侃侃而谈,具有相当的专业知识,特别是汽车领域的专业知识令人佩服。赵锡永是个骗子不假,但却做了官员和学者应该做的事情,由他引进的企业,经济效益都不差。在云南和湖南的地方企业人士看来,赵锡永假冒的只是1个司长,但他做的事情却是好事,于是干部都选择了不报案。面对着赵锡永这样的另类骗子,人们不仅要赞叹当今之官员和学者论水平和能力,远远不及赵锡永,赵锡永除了身份是假的,干的事情是真的,而那些身份是真的人,尸位素餐,又干了些多少让社会有益的事呢?社会在发展,产生骗子的土壤还肥沃着呢,这就是中国版的克鲁尔并非销声匿迹的原因。在艺术手法上,《大骗子克鲁尔的自白》也有其独到之处,作者让书中主人公翻然悔悟之后自述往事,这就更便于表达主人公许多思想感情和心理活动,能够使读者进入作者的内心世界,人物和情节紧紧围绕主人公这条线展开,从童年写起,更有对大骗子是怎样炼成的增加了说服力。其次在人物风情的描写上,也充分显示了作者高度造诣,寥寥数笔、不蔓不枝,就能使1个人物栩栩如生地跃然纸上。小说谴词造句讲究,善于使用长句,寓意深刻,哲理性强,充分体现德意志民族长于思辩的特色,深邃的哲理与辛辣的讽刺并存,虽经翻译,但仍不失原来的韵味。然而令人遗憾的是,正如这部原著副标题《回忆录第一部》所表明的,这部作品仅仅是托马斯.曼要写的整个计划的第一部分。从作家在作品中铺陈的场面来看,作者的意图是相当庞大的,主人公克鲁尔是在40岁左右开始回忆自己的童年的,作者从这时写起,才刚刚追溯到20岁左右,即写到行骗的第一站,在葡萄牙首都里斯本的活动。按计划主人公还要游历南北美洲、日本、非洲、南欧等地,直到他锒铛入狱,也就是说,还要历时二十年左右时间,作者如能如愿,那么展现在我们面前的不仅仅是十九世纪末的德国、巴黎和里斯本,而是大半个世界,那将是一本多么恢弘的煌煌巨著啊!但是尽管如此,本书的从内容还是结构上来看,仍不失为1个独立的整体。只是写了克鲁尔行骗的初级阶段,但读后对这样1个大骗子的本质和手法,已经初见端倪。纵观整个成书过程来看,本书是托马斯.曼最具个人特色的作品,因为它凝结了作者一生的大量心血,无论在内容上和艺术手法上都应该引起我们的充分重视。《大骗子克鲁尔的自白》摘录:*我想指出的是,我们的贪欲越强烈,也就是说我们越是执著地依附于世界所提供的一切,我们就越容易感到厌恶。*这是1大群可怜的谷蛾与蚊虫,正在无声地、盲目地扑向燃烧的火焰!它们怀着美好的愿望,是多么齐心一致地甘愿受诱骗!这里显然存在着1种由上帝亲自灌输给人本性中的普遍欲望,米勒-罗塞的本领正是为满足这种欲望而炼就的。毫无疑问,这里也存在着某种维持社会生活所不可缺少的结构,而这个人正是作为这个结构的公仆存在着和得到报偿。*因为只有如饥似渴地追求着的人才是可爱的,而不是已得到满足的人。*不过,他的感官是活跃的,他的精神在紧张地注意着一切;他在观赏着、享受着和吸收着。如果说潮涌般的喧嚣和印象使得这个来自沉寂得欲睡的农村小孩开始时感到眼花缭乱、不知所措,甚至畏首畏尾,那么,他还是具有足够的天赋和智力,能够逐渐从精神上应付得了这种嘈杂纷乱,并使之有助于自己去求知和如饥似渴的学习的欲望。*语言是神秘的东西的敌人,也是平庸无奇的东西的无情揭露者。*手段具有较为狭隘的专门意义,而目的只有较为普遍的意义。*有人说,法国人喜欢并尊重谈话——的确如此!难道不正是语言使人有别于动物吗?这样的观点并非没有道理:1个人讲话的水平越高,那他脱离动物状态也就越远。*人们都说“贫穷不是耻辱”,但只不过是说说而已。在那些阔绰富有的人看来,贫穷是极其可怕的,也可以说一半是污点一半是一般性的斥责,总而言之是非常令人厌恶的。因此,同贫穷沾了边就很可能导致令人不悦的后果。*过一段时间人同环境还是可以勉强协调起来的,甚至可以说,环境即便起初显得很艰苦——尽管不是对每个人,但至少对那些比较幸运的人来说是这样——但到头来还是有一定可塑性的,这不完全是1个习惯和适应的问题。同样的条件对每1个人说来,并不都是一样的,我甚至认为,普遍存在的事物,由于各个人处置不同,也会呈现出很不相同的形态。*所有美好的东西都是愚笨的,因为它仅仅是1种存在而已,是人的精神加以颂扬的对象。*事实确是如此:1个人一旦有了自命不凡这种与其说是充满自豪感不如说生来就具备的畏怯感情时,他就会在自己的周围造成1个隔离层和1种冷若冰霜的气氛,使得与他人建立诚挚友谊和伙伴关系的任何念头,不知为什么就会憋在心里并窒息掉了,对此,他本人也几乎会感到遗憾的。*1个人只要心里记住“暂时”这个词儿,对这种不合意的工作强做笑脸,同那些满意这个工作的同事和睦相处,那是不难做到的。人们尽管都标榜赞成平等,其实就其天性来说,还是倾向不平等,喜欢得天独厚的东西。*1个有教养的人,总是泰然自若地应付着一切,不假思索地承认既成事实,这是1个深通人情世故的人的特征。*那些狐狸和獾的洞穴和构造得非常精致的鸟巢却显示了更多的智慧和技能。当然,所有这些洞穴和鸟巢只不过是为了1个实用目的——藏身与繁衍后代,仅此而已。篇二 : Linux环境下段错误的产生原因及调试方法小结最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多、花费时间最长的问题就是著名的&段错误&(Segmentation Fault)。()借此机会系统学习了一下,这里对Linux环境下的段错误做个小结,方便以后同类问题的排查与解决。1. 段错误是什么一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址、访问了系统保护的内存地址、访问了只读的内存地址等等情况。这里贴一个对于&段错误&的准确定义(参考):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.2. 段错误产生的原因2.1 访问不存在的内存地址#include&stdio.h&#include&stdlib.h&void main(){int *ptr = NULL;*ptr = 0;}2.2 访问系统保护的内存地址#include&stdio.h&#include&stdlib.h&void main(){int *ptr = (int *)0;*ptr = 100;}2.3 访问只读的内存地址#include&stdio.h&#include&stdlib.h&#include&string.h&void main(){char *ptr = "test";strcpy(ptr, "TEST");}2.4 栈溢出#include&stdio.h&#include&stdlib.h&void main(){main();}等等其他原因。3. 段错误信息的获取程序发生段错误时,提示信息很少,下面有几种查看段错误的发生信息的途径。3.1 dmesgdmesg可以在应用程序crash掉时,显示内核中保存的相关信息。如下所示,通过dmesg命令可以查看发生段错误的程序名称、引起段错误发生的内存地址、指令指针地址、堆栈指针地址、错误代码、错误原因等。以程序2.3为例:panfeng@ubuntu:~/segfault$ dmesg[ ] segfault3[2700]: segfault at 80484e0 ip 00d2906a sp bfbbec3c error 7 in libc-2.10.1.so[cb]3.2 -g使用gcc编译程序的源码时,加上-g参数,这样可以使得生成的二进制文件中加入可以用于gdb调试的有用信息。以程序2.3为例:panfeng@ubuntu:~/segfault$ gcc -g -o segfault3 segfault3.c3.3 nm使用nm命令列出二进制文件中的符号表,包括符号地址、符号类型、符号名等,这样可以帮助定位在哪里发生了段错误。以程序2.3为例:panfeng@ubuntu:~/segfault$ nm segfault308049f20 d _DYNAMIC08049ff4 d _GLOBAL_OFFSET_TABLE_080484dc R _IO_stdin_usedw _Jv_RegisterClasses08049f10 d __CTOR_END__08049f0c d __CTOR_LIST__08049f18 D __DTOR_END__08049f14 d __DTOR_LIST__080484ec r __FRAME_END__08049f1c d __JCR_END__08049f1c d __JCR_LIST__ A __bss_start0804a00c D __data_start t __do_global_ctors_aux t __do_global_dtors_aux D __dso_handlew __gmon_start__0804848a T __i686.get_pc_thunk.bx08049f0c d __init_array_end08049f0c d __init_array_start T __libc_csu_fini T __libc_csu_initU __libc_start_main@@GLIBC_2.0 A _edata0804a01c A _end080484bc T _fini R _fp_hw080482bc T _init T _start b completed.69900804a00c W data_start b dtor_idx.6992 t frame_dummy T mainU memcpy@@GLIBC_2.03.4 ldd使用ldd命令查看二进制程序的共享链接库依赖,包括库的名称、起始地址,这样可以确定段错误到底是发生在了自己的程序中还是依赖的共享库中。以程序2.3为例:panfeng@ubuntu:~/segfault$ ldd ./segfault3linux-gate.so.1 =&
(0x00e08000)libc.so.6 =& /lib/tls/i686/cmov/libc.so.6 (0x)/lib/ld-linux.so.2 (0x)4. 段错误的调试方法4.1 使用printf输出信息扩展:linux实训小结 / 大班个别化环境小结 / 职业生涯环境分析小结这个是看似最简单但往往很多情况下十分有效的调试方式,也许可以说是程序员用的最多的调试方式。简单来说,就是在程序的重要代码附近加上像printf这类输出信息,这样可以跟踪并打印出段错误在代码中可能出现的位置。为了方便使用这种方法,可以使用条件编译指令#ifdef DEBUG和#endif把printf函数包起来。这样在程序编译时,如果加上-DDEBUG参数就能查看调试信息;否则不加该参数就不会显示调试信息。4.2 使用gcc和gdb4.2.1 调试步骤&1、为了能够使用gdb调试程序,在编译阶段加上-g参数,以程序2.3为例:panfeng@ubuntu:~/segfault$ gcc -g -o segfault3 segfault3.c2、使用gdb命令调试程序:panfeng@ubuntu:~/segfault$ gdb ./segfault3GNU gdb (GDB) 7.0-ubuntuCopyright (C) 2009 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later &&This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.
Type "show copying"and "show warranty" for details.This GDB was configured as "i486-linux-gnu".For bug reporting instructions, please see:&http://www.gnu.org/software/gdb/bugs/&...Reading symbols from /home/panfeng/segfault/segfault3...done.(gdb)3、进入gdb后,运行程序:(gdb) runStarting program: /home/panfeng/segfault/segfault3Program received signal SIGSEGV, Segmentation fault.0x001a306a in memcpy () from /lib/tls/i686/cmov/libc.so.6(gdb)从输出看出,程序2.3收到SIGSEGV信号,触发段错误,并提示地址0x001a306a、调用memcpy报的错,位于/lib/tls/i686/cmov/libc.so.6库中。4、完成调试后,输入quit命令退出gdb:(gdb) quitA debugging session is active.Inferior 1 [process 3207] will be killed.Quit anyway? (y or n) y4.2.2 适用场景1、仅当能确定程序一定会发生段错误的情况下使用。2、当程序的源码可以获得的情况下,使用-g参数编译程序。3、一般用于测试阶段,生产环境下gdb会有副作用:使程序运行减慢,运行不够稳定,等等。4、即使在测试阶段,如果程序过于复杂,gdb也不能处理。4.3 使用core文件和gdb在4.2节中提到段错误会触发SIGSEGV信号,通过man&7&signal,可以看到SIGSEGV默认的handler会打印段错误出错信息,并产生core文件,由此我们可以借助于程序异常退出时生成的core文件中的调试信息,使用gdb工具来调试程序中的段错误。4.3.1 调试步骤1、在一些Linux版本下,默认是不产生core文件的,首先可以查看一下系统core文件的大小限制:panfeng@ubuntu:~/segfault$ ulimit -c02、可以看到默认设置情况下,本机Linux环境下发生段错误时不会自动生成core文件,下面设置下core文件的大小限制(单位为KB):panfeng@ubuntu:~/segfault$ ulimit -c 1024panfeng@ubuntu:~/segfault$ ulimit -c10243、运行程序2.3,发生段错误生成core文件:panfeng@ubuntu:~/segfault$ ./segfault3段错误 (core dumped)4、加载core文件,使用gdb工具进行调试:panfeng@ubuntu:~/segfault$ gdb ./segfault3 ./coreGNU gdb (GDB) 7.0-ubuntuCopyright (C) 2009 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later &&This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.
Type "show copying"and "show warranty" for details.This GDB was configured as "i486-linux-gnu".For bug reporting instructions, please see:&http://www.gnu.org/software/gdb/bugs/&...Reading symbols from /home/panfeng/segfault/segfault3...done.warning: Can't read pathname for load map: 输入/输出错误.Reading symbols from /lib/tls/i686/cmov/libc.so.6...(no debugging symbols found)...done.Loaded symbols for /lib/tls/i686/cmov/libc.so.6Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.Loaded symbols for /lib/ld-linux.so.2Core was generated by `./segfault3'.Program terminated with signal 11, Segmentation fault.#0
0x0018506a in memcpy () from /lib/tls/i686/cmov/libc.6从输出看出,同4.2.1中一样的段错误信息。5、完成调试后,输入quit命令退出gdb:(gdb) quit4.3.2 适用场景1、适合于在实际生成环境下调试程序的段错误(即在不用重新发生段错误的情况下重现段错误)。2、当程序很复杂,core文件相当大时,该方法不可用。4.4 使用objdump4.4.1 调试步骤1、使用dmesg命令,找到最近发生的段错误输出信息:panfeng@ubuntu:~/segfault$ dmesg... ...[] segfault3[3320]: segfault at 80484e0 ip 0018506a sp bfc1cd6c error 7 in libc-2.10.1.so[e000]其中,对我们接下来的调试过程有用的是发生段错误的地址:80484e0和指令指针地址:0018506a。2、使用objdump生成二进制的相关信息,重定向到文件中:扩展:linux实训小结 / 大班个别化环境小结 / 职业生涯环境分析小结panfeng@ubuntu:~/segfault$ objdump -d ./segfault3 & segfault3Dump其中,生成的segfault3Dump文件中包含了二进制文件的segfault3的汇编代码。3、在segfault3Dump文件中查找发生段错误的地址:panfeng@ubuntu:~/segfault$ grep -n -A 10 -B 10 "80484e0" ./segfault3Dump121- 80483df:
*%eax122- 80483e1:
leave123- 80483e2:
ret124- 80483e3:
nop125-126- &main&:127- 80483e4:
%ebp128- 80483e5:
%esp,%ebp129- 80483e7:
$0xfffffff0,%esp130- 80483ea:
$0x20,%esp131: 80483ed:
c7 44 24 1c e0 84 04
$0xx1c(%esp)132- 80483f4:
08133- 80483f5:
b8 e5 84 04 08
$0x80484e5,%eax134- 80483fa:
c7 44 24 08 05 00 00
$0x5,0x8(%esp)135- 8048401:
00136- 8048402:
89 44 24 04
%eax,0x4(%esp)137- 8048406:
8b 44 24 1c
0x1c(%esp),%eax138- 804840a:
%eax,(%esp)139- 804840d:
e8 0a ff ff ff
804831c &memcpy@plt&140- 8048412:
leave141- 8048413:
ret通过对以上汇编代码分析,得知段错误发生main函数,对应的汇编指令是movl $0xx1c(%esp),接下来打开程序的源码,找到汇编指令对应的源码,也就定位到段错误了。4.4.2 适用场景1、不需要-g参数编译,不需要借助于core文件,但需要有一定的汇编语言基础。2、如果使用了gcc编译优化参数(-O1,-O2,-O3)的话,生成的汇编指令将会被优化,使得调试过程有些难度。4.5 使用catchsegvcatchsegv命令专门用来扑获段错误,它通过动态加载器(ld-linux.so)的预加载机制(PRELOAD)把一个事先写好的库(/lib/libSegFault.so)加载上,用于捕捉断错误的出错信息。panfeng@ubuntu:~/segfault$ catchsegv ./segfault3Segmentation fault (core dumped)*** Segmentation faultRegister dump:EAX:
EBX: 00fb3ff4
EBP: bfb7ad38
ESP: bfb7ad0cEIP: 00ee806a
EFLAGS: CS: 0073
SS: 007bTrap: 0000000e
OldMask: ESP/signal: bfb7ad0c
CR2: Backtrace:/lib/libSegFault.so[0x3b606f]??:0(??)[0xc76400]/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xe89b56]/build/buildd/eglibc-2.10.1/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8048351]Memory map:73000 r-xp :01 157 /lib/ld-2.10.1.so74000 r--p :01 157 /lib/ld-2.10.1.so75000 rw-p :01 157 /lib/ld-2.10.1.so003b0 r-xp :01 13105 /lib/libSegFault.so003b0 r--p :01 13105 /lib/libSegFault.so003b0 rw-p :01 13105 /lib/libSegFault.so00c00 r-xp :00 0 [vdso]00e0d000-00e29000 r-xp :01 4817 /lib/libgcc_s.so.100ea000 r--p :01 4817 /lib/libgcc_s.so.100e2a000-00e2b000 rw-p :01 4817 /lib/libgcc_s.so.100e00 r-xp :01 1800 /lib/tls/i686/cmov/libc-2.10.1.so00fb00 ---p :01 1800 /lib/tls/i686/cmov/libc-2.10.1.so00fb00 r--p :01 1800 /lib/tls/i686/cmov/libc-2.10.1.so00fb00 rw-p :01 1800 /lib/tls/i686/cmov/libc-2.10.1.so00fb00 rw-p :00 049000 r-xp :01 303895 /home/panfeng/segfault/segfault34a000 r--p :01 303895 /home/panfeng/segfault/segfault34b000 rw-p :01 303895 /home/panfeng/segfault/segfault357000 rw-p :00 0 [heap]b78cf000-b78d1000 rw-p :00 0b78df000-b78e1000 rw-p :00 0bfb67000-bfb7c000 rw-p :00 0 [stack]5. 一些注意事项1、出现段错误时,首先应该想到段错误的定义,从它出发考虑引发错误的原因。2、在使用指针时,定义了指针后记得初始化指针,在使用的时候记得判断是否为NULL。3、在使用数组时,注意数组是否被初始化,数组下标是否越界,数组元素是否存在等。4、在访问变量时,注意变量所占地址空间是否已经被程序释放掉。5、在处理变量时,注意变量的格式控制是否合理等。6. 参考资料列表1、2、http://blog.chinaunix.net/space.php?uid=317451&do=blog&id=92412扩展:linux实训小结 / 大班个别化环境小结 / 职业生涯环境分析小结
上一篇文章:
下一篇文章:
本文标题:[环境污染产生的原因]骗子产生的原因和环境——读《大骗子克鲁尔的自白》&版权说明
文章标题: 文章地址:
1、《[环境污染产生的原因]骗子产生的原因和环境——读《大骗子克鲁尔的自白》》一文由262阅读网()网友提供,版权归原作者本人所有,转载请注明出处!
2、转载或引用本网内容必须是以新闻性或资料性公共免费信息为使用目的的合理、善意引用,不得对本网内容原意进行曲解、修改,同时必须保留本网注明的"稿件来源",并自负版权等法律责任。
3、对于不当转载或引用本网内容而引起的民事纷争、行政处理或其他损失,本网不承担责任。Linux gdb命令教程
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师完成下面4个方面的功能: 启动程序,可以按照工程师自定义的要求随心所欲的运行程序。 让被调试的程序
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师完成下面4个方面的功能:
启动程序,可以按照工程师自定义的要求随心所欲的运行程序。
让被调试的程序在工程师指定的断点处停住,断点可以是条件表达式。
当程序被停住时,可以检查此时程序中所发生的事,并追索上文。
动态地改变程序的执行环境。
不管是调试内核空间的驱动还是调试用户空间的应用程序,掌握gdb的用法都是必须。而且,调试内核和调试应用程序时使用的gdb命令是完全相同的,下面以代码清单22.2的应用程序为例演示gdb调试器的用法。
int add(int a, int b)
return a +
int sum[10] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
int array1[10] =
48, 56, 77, 33, 33, 11, 226, 544, 78, 90
int array2[10] =
85, 99, 66, 0x199, 393, 11, 1, 2, 3, 4
for (i = 0; i & 10; i++)
sum[i] = add(array1[i], array2[i]);
使用命令gcc &g gdb_example.c &o gdb_example编译上述程序,得到包含调试信息的二进制文件example,执行gdb gdb_example命令进入调试状态:
[root@localhost driver_study]# gdb gdb_example
GNU gdb Red Hat Linux (5.3post-0.rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type &show copying& to see the conditions.
There is absolutely no warranty for GDB.
Type &show warranty& for details.
This GDB was configured as &i386-redhat-linux-gnu&...
1、list命令
在gdb中运行list命令(缩写l)可以列出代码,list的具体形式包括:
list &linenum& ,显示程序第linenum行周围的源程序,如:
(gdb) list 15
int array1[10] =
48, 56, 77, 33, 33, 11, 226, 544, 78, 90
int array2[10] =
85, 99, 66, 0x199, 393, 11, 1, 2, 3, 4
list &function& ,显示函数名为function的函数的源程序,如:
(gdb) list main
return a +
int sum[10];
int array1[10] =
ist,显示当前行后面的源程序。
list - ,显示当前行前面的源程序。
下面演示了使用gdb中的run(缩写r)、break(缩写b)、next(缩写n)命令控制程序的运行,并使用print(缩写p)命令打印程序中的变量sum的过程:
(gdb) break add
Breakpoint 1 at 0x80482f7:
gdb_example.c, line 3.
Starting program: /driver_study/gdb_example
Breakpoint 1, add (a=48, b=85) at gdb_example.c:3
warning: Source file is more recent than executable.
return a +
(gdb) next
(gdb) next
main () at gdb_example.c:23
for (i = 0; i & 10; i++)
(gdb) next
sum[i] = add(array1[i], array2[i]);
(gdb) print sum
$1 = {133, 0, 0, 0, 0, 0, 0, 0, 0, 0}
2、run命令
在gdb中,运行程序使用run命令。在程序运行前,我们可以设置如下4方面的工作环境:
&&& 程序运行参数
set args 可指定运行时参数,如:set args 10 20 30 40 50;show args 命令可以查看设置好的运行参数。
&&& 运行环境
path &dir& 可设定程序的运行路径;how paths可查看程序的运行路径;set environment varname [=value]用于设置环境变量,如set env USER=baohua;
show environment [varname]则用于查看环境变量。
&&& 工作目录
cd &dir& 相当于shell的cd命令;pwd 显示当前所在的目录。
&&& 程序的输入输出
info terminal 用于显示程序用到的终端的模式;gdb中也可以使用重定向控制程序输出,如run & outfile;
tty命令可以指定输入输出的终端设备,如:tty /dev/ttyS1。
3、break命令
在gdb中用break命令来设置断点,设置断点的方法包括:
&&& break &function&
在进入指定函数时停住,C++中可以使用class::function或function(type, type)格式来指定函数名。
&&& break &linenum&
在指定行号停住。
&&& break +offset / break -offset
在当前行号的前面或后面的offset行停住,offiset为自然数。
&&& break filename:linenum
在源文件filename的linenum行处停住。
&&& break filename:function
在源文件filename的function函数的入口处停住。
&&& break *address
在程序运行的内存地址处停住。
break命令没有参数时,表示在下一条指令处停住。
&&& break ... if &condition&
&...&可以是上述的break &linenum&、break +offset / break &offset中的参数,condition表示条件,在条件成立时停住。比如在循环体中,可以设置break if i=100,表示当i为100时停住程序。
查看断点时,可使用info命令,如info breakpoints [n]、info break [n](n表示断点号)。
4、单步命令
在调试过程中,next命令用于单步执行,类似VC++中的step over。next的单步不会进入函数的内部,与next对应的step(缩写s)命令则在单步执行一个函数时,会进入其内部,类似VC++中的step into。下面演示了step命令的执行情况,在23行的add()函数调用处执行step会进入其内部的&return a+b;&语句:
(gdb) break 25
Breakpoint 1 at 0x8048362: file gdb_example.c, line 25.
Starting program: /driver_study/gdb_example
Breakpoint 1, main () at gdb_example.c:25
sum[i] = add(array1[i], array2[i]);
(gdb) step
add (a=48, b=85) at gdb_example.c:3
return a +
单步执行的更复杂用法包括:
&&& step &count&
单步跟踪,如果有函数调用,则进入该函数(进入函数的前提是,此函数被编译有debug信息)。step后面不加count表示一条条地执行,加表示执行后面的count条指令,然后再停住。
&&& next &count&
单步跟踪,如果有函数调用,它不会进入该函数。同样地,next后面不加count表示一条条地执行,加表示执行后面的count条指令,然后再停住。
&&& set step-mode
set step-mode on用于打开step-mode模式,这样,在进行单步跟踪时,程序不会因为没有debug信息而不停住,这个参数的设置可便于查看机器码。set step-mod off用于关闭step-mode模式。
&&& finish
运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
&&& until (缩写u)
一直在循环体内执行单步,退不出来是一件令人烦恼的事情,until命令可以运行程序直到退出循环体。
&&& stepi(缩写si)和nexti(缩写ni)
stepi和nexti用于单步跟踪一条机器指令,一条程序代码有可能由数条机器指令完成,stepi和nexti可以单步执行机器指令。 另外,运行&display/i $pc&命令后,单步跟踪会在打出程序代码的同时打出机器指令,即汇编代码。
5、continue命令
当程序被停住后,可以使用continue命令(缩写c,fg命令同continue命令)恢复程序的运行直到程序结束,或到达下一个断点,命令格式为:
continue [ignore-count]
c [ignore-count]
fg [ignore-count]
ignore-count表示忽略其后多少次断点。 假设我们设置了函数断点add(),并watch i,则在continue过程中,每次遇到add()函数或i发生变化,程序就会停住,如:
(gdb) continue
Continuing.
Hardware watchpoint 3: i
Old value = 2
New value = 3
0x0804838d in main () at gdb_example.c:23
for (i = 0; i & 10; i++)
(gdb) continue
Continuing.
Breakpoint 1, main () at gdb_example.c:25
sum[i] = add(array1[i], array2[i]);
(gdb) continue
Continuing.
Hardware watchpoint 3: i
Old value = 3
New value = 4
0x0804838d in main () at gdb_example.c:23
for (i = 0; i & 10; i++)
6、print命令
在调试程序时,当程序被停住时,可以使用print命令(缩写为p),或是同义命令inspect来查看当前程序的运行数据。print命令的格式是:
print &expr&
print /&f& &expr&
&expr&是表达式,是被调试的程序中的表达式,&f&是输出的格式,比如,如果要把表达式按16进制的格式输出,那么就是/x。在表达式中,有几种GDB所支持的操作符,它们可以用在任何一种语言中,&@&是一个和数组有关的操作符,&::&指定一个在文件或是函数中的变量,&{&type&} &addr&&表示一个指向内存地址&addr&的类型为type的一个对象。
下面演示了查看sum[]数组的值的过程:
(gdb) print sum
$2 = {133, 155, 0, 0, 0, 0, 0, 0, 0, 0}
(gdb) next
Breakpoint 1, main () at gdb_example.c:25
sum[i] = add(array1[i], array2[i]);
(gdb) next
for (i = 0; i & 10; i++)
(gdb) print sum
$3 = {133, 155, 143, 0, 0, 0, 0, 0, 0, 0}
当需要查看一段连续内存空间的值的时间,可以使用GDB的&@&操作符,&@&的左边是第一个内存地址,&@&的右边则是想查看内存的长度。例如如下动态申请的内存:
int *array = (int *) malloc (len * sizeof (int));
在GDB调试过程中这样显示出这个动态数组的值:
p *array@len
print的输出格式包括:
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
我们可用display命令设置一些自动显示的变量,当程序停住时,或是单步跟踪时,这些变量会自动显示。 如果要修改变量,如x的值,可使用如下命令:
当用GDB的print查看程序运行时的数据时,每一个print都会被GDB记录下来。GDB会以$1,$2,$3 &这样的方式为每一个print命令编号。我们可以使用这个编号访问以前的表达式,如$1。
7、watch命令
watch一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序。我们有下面的几种方法来设置观察点: watch &expr&:为表达式(变量)expr设置一个观察点。一量表达式值有变化时,马上停住程序。rwatch &expr&:当表达式(变量)expr被读时,停住程序。awatch &expr&:当表达式(变量)的值被读或被写时,停住程序。info watchpoints:列出当前所设置了的所有观察点。 下面演示了观察i并在连续运行next时一旦发现i变化,i值就会显示出来的过程:
(gdb) watch i
Hardware watchpoint 3: i
(gdb) next
for (i = 0; i & 10; i++)
(gdb) next
Hardware watchpoint 3: i
Old value = 0
New value = 1
0x0804838d in main () at gdb_example.c:23
for (i = 0; i & 10; i++)
(gdb) next
Breakpoint 1, main () at gdb_example.c:25
sum[i] = add(array1[i], array2[i]);
(gdb) next
for (i = 0; i & 10; i++)
(gdb) next
Hardware watchpoint 3: i
Old value = 1
New value = 2
0x0804838d in main () at gdb_example.c:23
for (i = 0; i & 10; i++)
8、examine命令
我们可以使用examine命令(缩写为x)来查看内存地址中的值。examine命令的语法如下所示:
x/&n/f/u& &addr&
&addr&表示一个内存地址。&x/&后的n、f、u都是可选的参数,n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容;f 表示显示的格式,如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i;u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4字节。u参数可以被一些字符代替:b表示单字节,h表示双字节,w表示四字节,g表示八字节。当我们指定了字节长度后,GDB会从指定的内存地址开始,读写指定字节,并把其当作一个值取出来。n、f、u这3个参数可以一起使用,例如命令&x/3uh 0x54320&表示从内存地址0x54320开始以双字节为1个单位(h)、16进制方式(u)显示3个单位(3)的内存。 ==
譬如下面的例子:
char *c = &hello world&;
printf(&%s\n&, c);
char *c = &hello world&;
下一行设置断点后:
char *c = &hello world&;
printf(&%s\n&, c);
Breakpoint 1 at 0x: file main.c, line 4.
Starting program: /Users/songbarry/main
Reading symbols for shared libraries +. done
Breakpoint 1, main () at main.c:4
printf(&%s\n&, c);
可以通过多种方式看C指向的字符串:
$1 = 0xe &hello world&
(gdb) x/s 0xe
&hello world&
(gdb) p (char *)0xe
$3 = 0xe &hello world&
将第一个字符改为大写:
(gdb) p *(char *)0xe='H'
$4 = 72 'H'
$5 = 0xe &Hello world&
9、set命令
修改寄存器:
(gdb) set $v0 = 0x
(gdb) set $epc = 0xbfc00000
修改内存:
(gdb) set {unsigned int}0xx0
譬如对于第8节的例子:
(gdb) set {unsigned int}0xe=0x0
(gdb) x/10cb 0xe
0xe: 0 '\0' 0 '\0' 0 '\0' 0 '\0' 111 'o' 32 ' ' 119 'w' 111 'o'
0x: 114 'r' 108 'l'
$10 = 0xe &&
10、jump命令
一般来说,被调试程序会按照程序代码的运行顺序依次执行,但是GDB也提供了乱序执行的功能,也就是说,GDB可以修改程序的执行顺序,从而让程序随意跳跃。这个功能可以由GDB的jump命令:jump &linespec& 来指定下一条语句的运行点。&linespec&可以是文件的行号,可以是file:line格式,也可以是+num这种偏移量格式,表示下一条运行语句从哪里开始。jump &address& 这里的&address&是代码行的内存地址。 注意,jump命令不会改变当前的程序栈中的内容,所以,如果使用jump从一个函数跳转到另一个函数,当跳转到的函数运行完返回,进行出栈操作时必然会发生错误,这可能导致意想不到的结果,所以最好只用jump在同一个函数中进行跳转。
11、signal命令
使用singal命令,可以产生一个信号量给被调试的程序,如中断信号&Ctrl+C&。这非常方便于程序的调试,可以在程序运行的任意位置设置断点,并在该断点用GDB产生一个信号量,这种精确地在某处产生信号的方法非常有利于程序的调试。 signal命令的语法是:signal &signal&,UNIX的系统信号量通常从1到15,所以&signal&取值也在这个范围。
12、return命令
如果在函数中设置了调试断点,在断点后还有语句没有执行完,这时候我们可以使用return命令强制函数忽略还没有执行的语句并返回。
return &expression&
上述return命令用于取消当前函数的执行,并立即返回,如果指定了&expression&,那么该表达式的值会被作为函数的返回值。
13、call命令
call命令用于强制调用某函数: call &expr& 表达式中可以一是函数,以此达到强制调用函数的目的,它会显示函数的返回值(如果函数返回值不是void)。 其实,前面介绍的print命令也可以完成强制调用函数的功能。
14、info命令
info命令可以在调试时用来查看寄存器、断点、观察点和信号等信息。要查看寄存器的值,可以使用如下命令: info registers (查看除了浮点寄存器以外的寄存器)info all-registers (查看所有寄存器,包括浮点寄存器)info registers &regname ...& (查看所指定的寄存器) 要查看断点信息,可以使用如下命令:info break 列出当前所设置的所有观察点,使用如下命令:info watchpoints 查看有哪些信号正在被GDB检测,使用如下命令:info signals info handle 也可以使用info line命令来查看源代码在内存中的地址。info threads可以看多线程。info line后面可以跟行号、函数名、文件名:行号、文件名:函数名等多种形式,例如下面的命令会打印出所指定的源码在运行时的内存地址:
info line tst.c:func
15、set scheduler-locking off|on|step
off 不锁定任何线程,也就是所有线程都执行,这是默认值。
on 只有当前被调试程序会执行。
step 在单步的时候,除了next过一个函数的情况以外,只有当前线程会执行。
与多线程调试相关的命令还包括:
切换当前调试的线程为指定ID的线程。
break thread_test.c:123 thread all
在所有线程中相应的行上设置断点
thread apply ID1 ID2 command
让一个或者多个线程执行GDB命令command。
thread apply all command
让所有被调试线程执行GDB命令command。
16、disassemble
disassemble命令用于反汇编,它可被用来查看当前执行时的源代码的机器码,其实际上只是把目前内存中的指令dump出来。下面的示例用于查看函数func的汇编代码:
(gdb) disassemble func
Dump of assembler code for function func:
0x8048450 &func&:
0x8048451 &func+1&:
0x8048453 &func+3&:
$0x18,%esp
0x8048456 &func+6&:
$0x0,0xfffffffc(%ebp)
End of assembler dump.
转载请保留固定链接:
------分隔线----------------------------
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
在一般的 linux 或者 unix 系统中, 都可以通过编辑 bashrc 和 profil...
因为休眠功能在部分计算机无法正常工作,所以Ubuntu默认是不开...
看到too many open files可能想到fs.file-max参数,其实还受下面参数影...
为什么Nginx的性能要比Apache高很多? 这得益于Nginx使用了最新的...
老实说,人们最不曾思考的问题之一是他们的个人电脑中使用了...
近两年来,Linux的容器技术占据了世界企业IT市场的主导地位,并...

我要回帖

更多关于 找出错误读音 魔方格 的文章

 

随机推荐