热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

在Linux上找出并解决程序错误的主要方法

文章标题:在Linux上找出并解决程序错误的主要方法。Linux是中国IT实验室的一个技术频道。包含桌面应用,Linux系统管理,内核研究,嵌入式系统和开源等一些基本分类

  本文讨论了四种调试 Linux 程序的情况。在第 1 种情况中,我们使用了两个有内存分配问题的样本程序,使用 MEMWATCH 和 Yet Another Malloc Debugger(YAMD)工具来调试它们。在第 2 种情况中,我们使用了 Linux 中的 strace 实用程序,它能够跟踪系统调用和信号,从而找出程序发生错误的地方。在第 3 种情况中,我们使用 Linux 内核的 Oops 功能来解决程序的段错误,并向您展示如何设置内核源代码级调试器(kernel source level debugger,kgdb),以使用 GNU 调试器(GNU debugger,gdb)来解决相同的问题;kgdb 程序是使用串行连接的 Linux 内核远程 gdb。在第 4 种情况中,我们使用 Linux 上提供的魔术键控顺序(magic key sequence)来显示引发挂起问题的组件的信息。
  
  常见调试方法
  当您的程序中包含错误时,很可能在代码中某处有一个条件,您认为它为真(true),但实际上是假(false)。找出错误的过程也就是在找出错误后推翻以前一直确信为真的某个条件过程。
  
  以下几个示例是您可能确信成立的条件的一些类型:
  
  在源代码中的某处,某变量有特定的值。
  在给定的地方,某个结构已被正确设置。
  对于给定的 if-then-else 语句,if 部分就是被执行的路径。
  当子例程被调用时,该例程正确地接收到了它的参数。
  
  找出错误也就是要确定上述所有情况是否存在。如果您确信在子例程被调用时某变量应该有特定的值,那么就检查一下情况是否如此。如果您相信 if 结构会被执行,那么也检查一下情况是否如此。通常,您的假设都会是正确的,但最终您会找到与假设不符的情况。结果,您就会找出发生错误的地方。
  
  调试是您无法逃避的任务。进行调试有很多种方法,比如将消息打印到屏幕上、使用调试器,或只是考虑程序执行的情况并仔细地揣摩问题所在。
  
  在修正问题之前,您必须找出它的源头。举例来说,对于段错误,您需要了解段错误发生在代码的哪一行。一旦您发现了代码中出错的行,请确定该方法中变量的值、方法被调用的方式以及关于错误如何发生的详细情况。使用调试器将使找出所有这些信息变得很简单。如果没有调试器可用,您还可以使用其它的工具。(请注意,产品环境中可能并不提供调试器,而且 Linux 内核没有内建的调试器。)
  
  文将讨论一类通过人工检查代码不容易找到的问题,而且此类问题只在很少见的情况下存在。内存错误通常在多种情况同时存在时出现,而且您有时只能在部署程序之后才能发现内存错误。
  
  第 1 种情况:内存调试工具
  C 语言作为 Linux 系统上标准的编程语言给予了我们对动态内存分配很大的控制权。然而,这种自由可能会导致严重的内存管理问题,而这些问题可能导致程序崩溃或随时间的推移导致性能降级。
  
  内存泄漏(即 malloc() 内存在对应的 free() 调用执行后永不被释放)和缓冲区溢出(例如对以前分配到某数组的内存进行写操作)是一些常见的问题,它们可能很难检测到。这一部分将讨论几个调试工具,它们极大地简化了检测和找出内存问题的过程。
  
  MEMWATCH
  MEMWATCH 由 Johan Lindh 编写,是一个开放源代码 C 语言内存错误检测工具,您可以自己下载它(请参阅本文后面部分的参考资料)。只要在代码中添加一个头文件并在 gcc 语句中定义了 MEMWATCH 之后,您就可以跟踪程序中的内存泄漏和错误了。MEMWATCH 支持 ANSI C,它提供结果日志纪录,能检测双重释放(double-free)、错误释放(erroneous free)、没有释放的内存(unfreed memory)、溢出和下溢等等。
  
  清单 1. 内存样本(test1.c)
  #include
  #include
  #include "memwatch.h"
  
  int main(void)
  {
   char *ptr1;
   char *ptr2;
  
   ptr1 = malloc(512);
   ptr2 = malloc(512);
  
   ptr2 = ptr1;
   free(ptr2);
   free(ptr1);
  }
  
  清单 1 中的代码将分配两个 512 字节的内存块,然后指向第一个内存块的指针被设定为指向第二个内存块。结果,第二个内存块的地址丢失,从而产生了内存泄漏。
  
  现在我们编译清单 1 的 memwatch.c。下面是一个 makefile 示例:
  
  test1
  gcc -DMEMWATCH -DMW_STDIO test1.c memwatch
  c -o test1
  
  当您运行 test1 程序后,它会生成一个关于泄漏的内存的报告。清单 2 展示了示例 memwatch.log 输出文件。
  
  清单 2. test1 memwatch.log 文件
  MEMWATCH 2.67 Copyright (C) 1992-1999 Johan Lindh
  
  ...
  double-free: <4> test1.c(15), 0x80517b4 was freed from test1.c(14)
  ...
  unfreed: <2> test1.c(11), 512 bytes at 0x80519e4
  {FE FE FE FE FE FE FE FE FE FE FE FE ..............}
  
  Memory usage statistics (global):
   N)umber of allocations made: 2
   L)argest memory usage : 1024
   T)otal of all alloc() calls: 1024
   U)nfreed bytes totals : 512
  
  MEMWATCH 为您显示真正导致问题的行。如果您释放一个已经释放过的指针,它会告诉您。对于没有释放的内存也一样。日志结尾部分显示统计信息,包括泄漏了多少内存,使用了多少内存,以及总共分配了多少内存。
  
  YAMD
  YAMD 软件包由 Nate Eldredge 编写,可以查找 C 和 C++ 中动态的、与内存分配有关的问题。在撰写本文时,YAMD 的最新版本为 0.32。请下载 yamd-0.32.tar.gz(请参阅参考资料)。执行 make 命令来构建程序;然后执行 make install 命令安装程序并设置工具。
  
  一旦您下载了 YAMD 之后,请在 test1.c 上使用它。请删除 #include memwatch.h 并对 makefile 进行如下小小的修改:
  
  使用 YAMD 的 test1
  gcc -g test1.c -o test1
  
  清单 3 展示了来自 test1 上的 YAMD 的输出。
  
  清单 3. 使用 YAMD 的 test1 输出
  YAMD version 0.32
  Executable: /usr/src/test/yamd-0.32/test1
  ...
  INFO: Normal allocation of this block
  Address 0x40025e00, size 512
  ...
  INFO: Normal allocation of this block
  Address 0x40028e00, size 512
  ...
  INFO: Normal deallocation of this block
  Address 0x40025e00, size 512
  ...
  ERROR: Multiple freeing At
  free of pointer already freed
  Address 0x40025e00, size 512
  ...
  WARNING: Memory leak
  Address 0x40028e00, size 512
  WARNING: Total memory leaks:
  1 unfreed allocations totaling 512 bytes
  
  *** Finished at Tue ... 10:07:15 2002
  Allocated a grand total of 1024 bytes 2 allocations
  Average of 512 bytes per allocation
  Max bytes allocated at one time: 1024
  24 K alloced internally / 12 K mapped now / 8 K max
  Virtual program size is 1416 K
  End.
  
  YAMD 显示我们已经释放了内存,而且存在内存泄漏。让我们在清单 4 中另一个样本程序上试试 YAMD。
  
  清单 4. 内存代码(test2.c)
  #include
  #include
  
  int main(void)
  {
   char *ptr1;
   char *ptr2;
   char *chptr;
   int i = 1;
   ptr1 = malloc(512);
   ptr2 = malloc(512);
   chptr = (char *)malloc(512);
   for (i; i <= 512; i++) {
  chptr[i] = 'S';
   }
   ptr2 = ptr1;
   free(ptr2);
   free(ptr1);
   free(chptr);
  }
  
  您可以使用下面的命令来启动 YAMD:
  
  ./run-yamd /usr/src/test/test2/test2
  清单 5 显示了在样本程序 test2 上使用 YAMD 得到的输出。YAMD 告诉我们在 for 循环中有“越界(out-of-bounds)”的情况。
  
  清单 5. 使用 YAMD 的 test2 输出
  Running /usr/src/test/test2/test2
  Temp output to /tmp/yamd-out.1243
  *********
  ./run-yamd: line 101: 1248 Segmentation fault (core dumped)
  YAMD version 0.32
  Starting run: /usr/src/test/test2/test2
  Executable: /usr/src/test/test2/test2
  Virtual program size is 1380 K
  ...
  INFO: Normal allocation of this block
  Address 0x40025e00, size 512
  ...
  INFO: Normal allocation of this block
  Address 0x40028e00, size 512
  ...
  INFO: Normal allocation of this block
  Address 0x4002be00, size 512
  ERROR: Crash
  ...
  Tried to write address 0x4002c000
  Seems to be part of this block:
  Address 0x4002be00, size 512
  ...
  Address in question is at offset 512 (out of bounds)
  Will dump core after checking heap.
  Done.
  
  MEMWATCH 和 YAMD 都是很有用的调试工具,它们的使用方法有所不同。对于 MEMWATCH,您需要添加包含文件 memwatch.h 并打开两个编译时间标记。对于链接(link)语句,YAMD 只需要 -g 选项。
  
  Electric Fence
  多数 L
推荐阅读
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Linuxchmod目录权限命令图文详解在Linux文件系统模型中,每个文件都有一组9个权限位用来控制谁能够读写和执行该文件的内容。对于目录来说,执行位的作用是控制能否进入或者通过 ... [详细]
  • 近年来,大数据成为互联网世界的新宠儿,被列入阿里巴巴、谷歌等公司的战略规划中,也在政府报告中频繁提及。据《大数据人才报告》显示,目前全国大数据人才仅46万,未来3-5年将出现高达150万的人才缺口。根据领英报告,数据剖析人才供应指数最低,且跳槽速度最快。中国商业结合会数据剖析专业委员会统计显示,未来中国基础性数据剖析人才缺口将高达1400万。目前BAT企业中,60%以上的招聘职位都是针对大数据人才的。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文主要讨论了在xps15上安装双系统win10和MacOS后,win10无法正常更新的问题。分析了可能的引导问题,并提供了解决方法。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • 树莓派Linux基础(一):查看文件系统的命令行操作
    本文介绍了在树莓派上通过SSH服务使用命令行查看文件系统的操作,包括cd命令用于变更目录、pwd命令用于显示当前目录位置、ls命令用于显示文件和目录列表。详细讲解了这些命令的使用方法和注意事项。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • Python语法上的区别及注意事项
    本文介绍了Python2x和Python3x在语法上的区别,包括print语句的变化、除法运算结果的不同、raw_input函数的替代、class写法的变化等。同时还介绍了Python脚本的解释程序的指定方法,以及在不同版本的Python中如何执行脚本。对于想要学习Python的人来说,本文提供了一些注意事项和技巧。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
author-avatar
手机用户2502855477
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有