网站名词排名怎么做,seo关键词优化工具,怎么套用模板做网站,一比一高仿手表网站内存调试技巧 2007 年 6 月 21 日 本文将带您了解一些良好的和内存相关的编码实践#xff0c;以将内存错误保持在控制范围内。内存错误是 C 和 C 编程的祸根#xff1a;它们很普遍#xff0c;认识其严重性已有二十多年#xff0c;但始终没有彻底解决#xff0c;它们可能严…内存调试技巧 2007 年 6 月 21 日 本文将带您了解一些良好的和内存相关的编码实践以将内存错误保持在控制范围内。内存错误是 C 和 C 编程的祸根它们很普遍认识其严重性已有二十多年但始终没有彻底解决它们可能严重影响应用程序并且很少有开发团队对其制定明确的管理计划。但好消息是它们并不怎么神秘。 引言 C 和 C 程序中的内存错误非常有害它们很常见并且可能导致严重的后果。来自计算机应急响应小组请参见参考资料和供应商的许多最严重的安全公告都是由简单的内存错误造成的。自从 70 年代末期以来C 程序员就一直讨论此类错误但其影响在 2007 年仍然很大。更糟的是如果按我的思路考虑当今的许多 C 和 C 程序员可能都会认为内存错误是不可控制而又神秘的顽症它们只能纠正无法预防。 但事实并非如此。本文将让您在短时间内理解与良好内存相关的编码的所有本质 正确的内存管理的重要性内存错误的类别内存编程的策略结束语正确的内存管理的重要性 存在内存错误的 C 和 C 程序会导致各种问题。如果它们泄漏内存则运行速度会逐渐变慢并最终停止运行如果覆盖内存则会变得非常脆弱很容易受到恶意用户的攻击。从 1988 年著名的莫里斯蠕虫 攻击到有关 Flash Player 和其他关键的零售级程序的最新安全警报都与缓冲区溢出有关“大多数计算机安全漏洞都是缓冲区溢出”Rodney Bates 在 2004 年写道。 在可以使用 C 或 C 的地方也广泛支持使用其他许多通用语言如 Java、Ruby、Haskell、C#、Perl、Smalltalk 等每种语言都有众多的爱好者和各自的优点。但是从计算角度来看每种编程语言优于 C 或 C 的主要优点都与便于内存管理密切相关。与内存相关的编程是如此重要而在实践中正确应用又是如此困难以致于它支配着面向对象编程语言、功能性编程语言、高级编程语言、声明性编程语言和另外一些编程语言的所有其他变量或理论。 与少数其他类型的常见错误一样内存错误还是一种隐性危害它们很难再现症状通常不能在相应的源代码中找到。例如无论何时何地发生内存泄漏都可能表现为应用程序完全无法接受同时内存泄漏不是显而易见。 因此出于所有这些原因需要特别关注 C 和 C 编程的内存问题。让我们看一看如何解决这些问题先不谈是哪种语言。 内存错误的类别 首先不要失去信心。有很多办法可以对付内存问题。我们先列出所有可能存在的实际问题 内存泄漏 错误分配包括大量增加 free() 释放的内存和未初始化的引用 悬空指针 数组边界违规 这是所有类型。即使迁移到 C 面向对象的语言这些类型也不会有明显变化无论数据是简单类型还是 C 语言的 struct 或 C 的类C 和 C 中内存管理和引用的模型在原理上都是相同的。以下内容绝大部分是“纯 C”语言对于扩展到 C 主要留作练习使用。 内存泄漏 在分配资源时会发生内存泄漏但是它从不回收。下面是一个可能出错的模型请参见清单 1 清单 1. 简单的潜在堆内存丢失和缓冲区覆盖 void f1(char *explanation) { char p1; p1 malloc(100); (void) sprintf(p1, The f1 error occurred because of %s., explanation); local_log(p1); } 您看到问题了吗除非 local_log() 对 free() 释放的内存具有不寻常的响应能力否则每次对f1 的调用都会泄漏 100 字节。在记忆棒增量分发数兆字节内存时一次泄漏是微不足道的但是连续操作数小时后即使如此小的泄漏也会削弱应用程序。 在实际的 C 和 C 编程中这不足以影响您对 malloc() 或 new 的使用本部分开头的句子提到了“资源”不是仅指“内存”因为还有类似以下内容的示例请参见清单 2。FILE 句柄可能与内存块不同但是必须对它们给予同等关注 清单 2. 来自资源错误管理的潜在堆内存丢失 int getkey(char *filename) { FILE *fp; int key; fp fopen(filename, r); fscanf(fp, %d, key); return key; } fopen 的语义需要补充性的 fclose。在没有 fclose() 的情况下C 标准不能指定发生的情况时很可能是内存泄漏。其他资源如信号量、网络句柄、数据库连接等同样值得考虑。 内存错误分配 错误分配的管理不是很困难。下面是一个示例请参见清单 3 清单 3. 未初始化的指针 void f2(int datum) { int *p2; /* Uh-oh! No one has initialized p2. */ *p2 datum; ... } 关于此类错误的好消息是它们一般具有显著结果。在 AIX® 下对未初始化指针的分配通常会立即导致 segmentation fault 错误。它的好处是任何此类错误都会被快速地检测到与花费数月时间才能确定且难以再现的错误相比检测此类错误的代价要小得多。 在此错误类型中存在多个变种。free() 释放的内存比 malloc() 更频繁请参见清单 4 清单 4. 两个错误的内存释放 /* Allocate once, free twice. */ void f3() { char *p; p malloc(10); ... free(p); ... free(p); } /* Allocate zero times, free once. */ void f4() { char *p; /* Note that p remains uninitialized here. */ free(p); } 这些错误通常也不太严重。尽管 C 标准在这些情形中没有定义具体行为但典型的实现将忽略错误或者快速而明确地对它们进行标记总之这些都是安全情形。 悬空指针 悬空指针比较棘手。当程序员在内存资源释放后使用资源时会发生悬空指针请参见清单 5 清单 5. 悬空指针 void f8() { struct x *xp; xp (struct x *) malloc(sizeof (struct x)); xp.q 13; ... free(xp); ... /* Problem! Theres no guarantee that the memory block to which xp points hasnt been overwritten. */ return xp.q; } 传统的“调试”难以隔离悬空指针。由于下面两个明显原因它们很难再现 即使影响提前释放内存范围的代码已本地化内存的使用仍然可能取决于应用程序甚至在极端情况下不同进程中的其他执行位置。 悬空指针可能发生在以微妙方式使用内存的代码中。结果是即使内存在释放后立即被覆盖并且新指向的值不同于预期值也很难识别出新值是错误值。 悬空指针不断威胁着 C 或 C 程序的运行状态。 数组边界违规 数组边界违规十分危险它是内存错误管理的最后一个主要类别。回头看一下清单 1如果explanation 的长度超过 80则会发生什么情况回答难以预料但是它可能与良好情形相差甚远。特别是C 复制一个字符串该字符串不适于为它分配的 100 个字符。在任何常规实现中“超过的”字符会覆盖内存中的其他数据。内存中数据分配的布局非常复杂并且难以再现所以任何症状都不可能追溯到源代码级别的具体错误。这些错误通常会导致数百万美元的损失。 内存编程的策略 勤奋和自律可以让这些错误造成的影响降至最低限度。下面我们介绍一下您可以采用的几个特定步骤我在各种组织中处理它们的经验是至少可以按一定的数量级持续减少内存错误。 编码风格 编码风格是最重要的我还从没有看到过其他任何作者对此加以强调。影响资源特别是内存的函数和方法需要显式地解释本身。下面是有关标头、注释或名称的一些示例请参见清单 6。 清单 6. 识别资源的源代码示例 /******** * ... * * Note that any function invoking protected_file_read() * assumes responsibility eventually to fclose() its * return value, UNLESS that value is NULL. * ********/ FILE *protected_file_read(char *filename) { FILE *fp; fp fopen(filename, r); if (fp) { ... } else { ... } return fp; } /******* * ... * * Note that the return value of get_message points to a * fixed memory location. Do NOT free() it; remember to * make a copy if it must be retained ... * ********/ char *get_message() { static char this_buffer[400]; ... (void) sprintf(this_buffer, ...); return this_buffer; } /******** * ... * While this function uses heap memory, and so * temporarily might expand the over-all memory * footprint, it properly cleans up after itself. * ********/ int f6(char *item1) { my_class c1; int result; ... c1 new my_class(item1); ... result c1.x; delete c1; return result; } /******** * ... * Note that f8() is documented to return a value * which needs to be returned to heap; as f7 thinly * wraps f8, any code which invokes f7() must be * careful to free() the return value. * ********/ int *f7() { int *p; p f8(...); ... return p; } 使这些格式元素成为您日常工作的一部分。可以使用各种方法解决内存问题 专用库 语言 软件工具 硬件检查器 在这整个领域中我始终认为最有用并且投资回报率最大的是考虑改进源代码的风格。它不需要昂贵的代价或严格的形式可以始终取消与内存无关的段的注释但影响内存的定义当然需要显式注释。添加几个简单的单词可使内存结果更清楚并且内存编程会得到改进。 我没有做受控实验来验证此风格的效果。如果您的经历与我一样您将发现没有说明资源影响的策略简直无法忍受。这样做很简单但带来的好处太多了。 检测 检测是编码标准的补充。二者各有裨益但结合使用效果特别好。机灵的 C 或 C 专业人员甚至可以浏览不熟悉的源代码并以极低的成本检测内存问题。通过少量的实践和适当的文本搜索您能够快速验证平衡的*alloc() 和 free() 或者 new 和 delete 的源主体。人工查看此类内容通常会出现像清单 7 中一样的问题。 清单 7. 棘手的内存泄漏 static char *important_pointer NULL; void f9() { if (!important_pointer) important_pointer malloc(IMPORTANT_SIZE); ... if (condition) /* Ooops! We just lost the reference important_pointer already held. */ important_pointer malloc(DIFFERENT_SIZE); ... } 如果 condition 为真简单使用自动运行时工具不能检测发生的内存泄漏。仔细进行源分析可以从此类条件推理出证实正确的结论。我重复一下我写的关于风格的内容尽管大量发布的内存问题描述都强调工具和语言对于我来说最大的收获来自“软的”以开发人员为中心的流程变更。您在风格和检测上所做的任何改进都可以帮助您理解由自动化工具产生的诊断。 静态的自动语法分析 当然并不是只有人类才能读取源代码。您还应使静态语法分析 成为开发流程的一部分。静态语法分析是 lint、严格编译 和几种商业产品执行的内容扫描编译器接受的源文本和目标项但这可能是错误的症状。 希望让您的代码无 lint。尽管 lint 已过时并有一定的局限性但是没有使用它或其较高级的后代的许多程序员犯了很大的错误。通常情况下您能够编写忽略lint 的优秀的专业质量代码但努力这样做的结果通常会发生重大错误。其中一些错误影响内存的正确性。与让客户首先发现内存错误的代价相比即使对这种类别的产品支付最昂贵的许可费也失去了意义。清除源代码。现在即使lint 标记的编码可能向您提供所需的功能但很可能存在更简单的方法该方法可满足 lint并且比较强键又可移植。 内存库 补救方法的最后两个类别与前三个明显不同。前者是轻量级 的一个人可以容易地理解并实现它们。另一方面内存库和工具通常具有较高的许可费用对部分开发人员来说它们需要进一步完善和调整。有效地使用库和工具的程序员是理解轻量级的静态 方法的人员。可用的库和工具给人的印象很深其作为组的质量很高。但是即使最优秀的编程人员也可能会被忽略内存管理基本原则的非常任性的编程人员搅乱。据我观察普通的编程人员在尝试利用内存库和工具进行隔离工作时也只能感到灰心。 由于这些原因我们催促 C 和 C 程序员为解决内存问题先了解一下自己的源。在这完成之后才去考虑库。 使用几个库能够编写常规的 C 或 C 代码并保证改进内存管理。Jonathan Bartlett 在 developerWorks 的 2004 评论专栏中介绍了主要的候选项可以在下面的参考资料部分获得。库可以解决多种不同的内存问题以致于直接对它们进行比较是非常困难的这方面的常见主题包括垃圾收集、智能指针 和 智能容器。大体上说库可以自动进行较多的内存管理这样程序员可以犯更少的错误。 我对内存库有各种感受。他们在努力工作但我看到他们在项目中获得的成功比预期要小尤其在 C 方面。我尚未对这些令人失望的结果进行仔细分析。例如业绩应该与相应的手动 内存管理一样好但是这是一个灰色区域——尤其在垃圾收集库处理速度缓慢的情况下。通过这方面的实践得出的最明确的结论是与 C 关注的代码组相比C 似乎可以较好地接受智能指针。 内存工具 开发真正基于 C 的应用程序的开发团队需要运行时内存工具作为其开发策略的一部分。已介绍的技术很有价值而且不可或缺。在您亲自尝试使用内存工具之前其质量和功能您可能还不了解。 本文主要讨论了基于软件的内存工具。还有硬件内存调试器在非常特殊的情况下主要是在使用不支持其他工具的专用主机时才考虑它们。 市场上的软件内存工具包括专有工具如 IBM Rational® Purify 和 Electric Fence和其他开放源代码工具。其中有许多可以很好地与 AIX 和其他操作系统一起使用。 所有内存工具的功能基本相同构建可执行文件的特定版本很像在编译时通过使用 -g 标记生成的调试版本、练习相关应用程序和研究由工具自动生成的报告。请考虑如清单 8 所示的程序。 清单 8. 示例错误 int main() { char p[5]; strcpy(p, Hello, world.); puts(p); } 此程序可以在许多环境中“运行”它编译、执行并将“Hello, world.\n”打印到屏幕。使用内存工具运行相同应用程序会在第四行产生一个数组边界违规的报告。在了解软件错误将十四个字符复制到了只能容纳五个字符的空间中方面这种方法比在客户处查找错误症状的花费小得多。这是内存工具的功劳。 结束语 作为一名成熟的 C 或 C 程序员您认识到内存问题值得特别关注。通过制订一些计划和实践可以找到控制内存错误的方法。学习内存使用的正确模式快速发现可能发生的错误使本文介绍的技术成为您日常工作的一部分。您可以在开始时就消除应用程序中的症状否则可能要花费数天或数周时间来调试。 共享本文…… 请 Digg 该故事发布到 del.icio.usSlashdot 一下参考资料 学习 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。计算机应急响应团队计算机应急响应小组是“联邦政府资助的研发中心”通过该中心以及许多其他活动发布关于specific software vulnerabilities 的技术计算机安全警报。Why do good programmers follow bad practices?这是助理教授 Rodney Bates 为ACM Queue 撰写的一篇关于 C 编程和缓冲区溢出的文章。内存管理内幕developerWorks2004 年 11 月概述了对 Linux® 程序员有用的内存管理技术主要适用于 C 语言但是也适用于其他语言。Rational Purify学习更多关于此主要专有内存工具的内容。Coverity, Incorporated此站点提供产品和服务而且与 C 和 C 的静态源代码分析有关。Memory hygiene in C and C, Part 2:Commercial tools这是我在 2004 年撰写的一篇文章。我还维护personal notes on memory debuggers 的网页。AIX and UNIXAIX and UNIX developerWorks专区提供了大量与 AIX 系统管理的所有方面相关并扩展您的 UNIX 技能的信息。New to AIX and UNIX访问 New to AIX and UNIX 页面以了解更多关于 AIX 和 UNIX 的内容。AIX 5L? WikiAIX 相关技术信息的协作环境。查看 Cameron Laird 撰写的其他文章和教程。 整个 developerWorks按主题搜索“AIX and UNIX”库 系统管理应用程序开发性能移植安全性提示工具和实用程序Java technologyLinux开放源代码Safari 书店访问这个电子参考库以查找特定的技术资源。developerWorks 技术事件和网络广播了解最新的 developerWorks 技术事件和网络广播。Podcasts收听 Podcast 并与 IBM 技术专家保持同步。获得产品和技术 IBM 试用软件使用 IBM 试用软件开发您的下一个项目可直接从 developerWorks 下载这些试用软件。讨论 参与 developerWorks blogs从而加入到 developerWorks 社区中来。参与“AIX and UNIX”论坛 AIX 5L——技术论坛AIX for Developers 论坛集群系统管理IBM Support Assistant性能工具——技术虚拟化——技术更多“AIX and UNIX”论坛关于作者 Cameron Laird 是 developerWorks 长期投稿者和前专栏作家。他经常编写关于促进其公司应用程序开发的开源项目的文章主要关注可靠性和安全性。 转载于:https://www.cnblogs.com/ajian005/archive/2012/10/30/2753642.html