Linux-系统编程实用手册-九-

94 阅读9分钟

Linux 系统编程实用手册(九)

原文:zh.annas-archive.org/md5/9713B9F84CB12A4F8624F3E68B0D4320

译者:飞龙

协议:CC BY-NC-SA 4.0

第十九章:故障排除和最佳实践

本章重点介绍了较新的 Linux 故障排除工具和实用程序,以及在设计、开发和部署真实世界 Linux 系统应用程序时应遵循的行业最佳实践。但我们要非常清楚,这是一本关于 Linux 系统编程的书;这里描述的故障排除技巧和最佳实践仅与 Linux 系统上应用程序的系统级开发有关(通常用 C/C++编写);我们不涉及 Linux 上的通用故障排除(例如故障排除网络或配置问题、系统管理技巧和窍门)。

特别是对于本章(主要是由于内容的广泛范围和规模,它只是顺便提到),我们在 GitHub 存储库的“进一步阅读”部分提供了几篇有用的在线文章和书籍。请浏览一下。

本章旨在结束本书;在这里,关于 Linux 系统编程,读者将得到以下内容:

  • (较新)故障排除工具和技术概述

  • 设计、软件工程、编程实施和测试方面的行业最佳实践概述

故障排除工具

在本节中,我们将提到几个工具和实用程序,可以帮助应用程序开发人员识别系统瓶颈和性能问题。(请注意,在这里,为了节省空间和时间,我们不深入研究数十个常见的系统监控实用程序,如pspstreetophtoppidstatvmstatdstatsarnagiosiotopiostationicelsofnmoniftopethtoolnetstattcpdumpwireshark,而是提到了较新的工具)。在进行数据收集(或基准测试)以供后续分析时,有一件重要的事情需要记住:费心设置一个测试装置,并在使用时,尽可能只改变一个变量,以便在给定的运行中看到其影响。

perf

性能测量和分析是一个庞大的主题;对性能问题的识别、分析和确定根本原因并不是一项微不足道的任务。近年来,perf(1)htop(1)实用程序已成为 Linux 平台上性能测量和分析的基本工具。

有时,您只需要查看哪些进程占用了最多的 CPU;传统上,我们使用众所周知的top(1)实用程序来做到这一点。请尝试使用非常有用的perf变体,如:sudo perf top

此外,您还可以利用以下一些功能:

sudo perf top -r 90 --sort pid,comm,dso,symbol
 (-r 90 => collect data with SCHED_FIFO RT scheduling class and priority 90 [1-99]).

基本上,这是perf工作流程:记录一个会话(数据文件被保存)并生成报告。(请参阅 GitHub 存储库中“进一步阅读”部分中的链接。)

Brendan Gregg 的博客上提供了出色的图表,清楚地展示了 Linux 上可用于观察、性能分析和动态跟踪的数十种工具:

由于其视觉冲击力,Brendan Gregg 的 Flame Graph 脚本也非常有趣;请查看 GitHub 存储库中“进一步阅读”部分中的链接。

Brendan Gregg 还领导了一个名为 perf-tools 的项目。以下是该项目的一些话:基于 Linux perf_events(又名 perf)和 Ftrace 的性能分析工具。这些工具包括几个非常有用的 shell 脚本包装程序(覆盖了 Perf、Ftrace 和 Kprobes);请克隆 GitHub 存储库并尝试它们。(*github.com/brendangregg/perf-tools.)

跟踪工具

深入追踪往往会产生令人渴望的副作用,即开发人员或测试人员可以发现性能瓶颈,以及调试系统级的延迟和问题。Linux 有大量的框架和工具可用于跟踪,无论是在用户空间还是在内核级别;这里提到了一些更相关的工具:

  • 用户空间ltrace(1)(跟踪库 API),strace(1)(跟踪系统调用;也尝试使用sudo perf trace),LTTng-ust,uprobes。

  • 内核空间:LTTng,ftrace(以及几个前端,如tracecmd(1),kernelshark GUIm),Kprobes—(包括 Jprobes—直到内核版本 4.14),Kretprobes;SystemTaprm)eBPF。

Linux proc 文件系统

Linux 有一个非常丰富和强大的文件系统,称为procfsproc代表进程。它通常挂载在/proc下,包含伪文件和目录,其中包含有关进程和内部信息的有价值的运行时生成的信息。简而言之,procfs 用于两个关键目的的用户界面:

  • 它作为一个窗口,可以查看详细的进程、线程、操作系统和硬件信息。

  • 它用于查询和设置内核级可调参数(核心内核、调度、内存和网络参数的开关和值)的地方。

花时间学习和使用 Linux 的 proc 文件系统是非常值得的。几乎所有用户空间的监控和分析工具最终都是基于 procfs 的。在 GitHub 存储库的进一步阅读部分提供的链接中可以找到更多信息。

最佳实践

在本节中,我们简要列举了我们认为是行业最佳实践的内容,尽管它们大多是通用的,因此范围广泛;我们将特别从 Linux 系统程序员的角度来看待它们。

经验主义方法

根据《剑桥英语词典》,经验主义一词意味着基于所经历或所见,而不是基于理论。这可能是应该遵循的关键原则。Gustavo Duarte 在一篇引人入胜的文章中提到(在这里提到:www.infoq.com/news/2008/02/realitydrivendevelopment):“行动和实验是经验主义的基石。不会通过广泛的分析和大量的文档来压制现实。通过实验邀请现实。非经验主义公司花费一年时间,由 43 人规划一个关机按钮设计。”在整本书中,我们也一直试图有意识地遵循经验主义方法;我们强烈建议读者在设计和开发中培养和嵌入经验主义原则。

软件工程的智慧

Frederick P Brooks 在 1975 年写了他著名的论著《神话般的程序员月份:软件工程论文》,这本书至今被誉为对软件项目管理最有影响力的书。这并不奇怪:某些真理就是真理。以下是这本书中的一些精彩之处:

  • 计划扔掉一个;你无论如何都会扔掉一个。

  • 没有银弹。

  • 好菜需要时间。如果你被迫等待,那是为了更好地为你服务,让你满意。

  • 生孩子需要九个月,无论分配多少个女人。

  • 好判断来自经验,经验来自糟糕的判断。

有趣的是,当然,古老的 Unix 操作系统的设计哲学确实包含了伟大的设计原则,这些原则至今在 Linux 上仍然有效。我们在《Linux 系统架构》的章节《Unix 哲学概述》中有所涉及。

编程

现在让我们转向开发人员需要牢记的更世俗但非常重要的事情。

程序员的清单-七条规则

我们建议以下七条规则:

  • 规则#1:检查所有 API 的失败情况。

  • 规则#2:使用警告编译(-Wall -Wextra)并尽可能消除所有警告。

  • 规则#3:永远不要相信(用户)输入;验证它。

  • 规则#4:在你的代码中使用断言。

  • 规则#5:立即从代码库中消除未使用的(或死代码)。

  • 规则#6:彻底测试;100%的代码覆盖率是目标。花时间和精力学习使用强大的工具:内存检查器(Valgrind,sanitizer 工具集),静态和动态分析器,安全检查器(checksec),模糊器(见下面的解释)。

  • 规则#7:不要假设任何事情(假设会让都成为)。

以下是一些不遵循规则可能导致严重失败的例子:阿丽亚娜 5 号无人火箭在发射初期坠毁(1996 年 6 月 4 日);最终发现错误是由于寄存器溢出问题,一个单一的类型转换错误(规则#5)。Knight Capital Group 在 45 分钟内损失了 4.6 亿美元。不要假设页面的大小。使用getpagesize(2)系统调用或sysconf(3)来获取它。此外,还可以查看名为Low-Level Software Design的博客文章(GitHub 存储库的进一步阅读部分中有链接)。

更好的测试

测试是一项关键活动;彻底和持续的测试(包括回归测试)会导致一个稳定的产品,工程团队和客户都对其深信不疑。

这里有一个经常被忽视的事实:完整的代码覆盖测试至关重要!为什么?简单——通常会有隐藏的缺陷潜伏在从未被测试过的代码部分(错误处理是典型的例子);事实是,它们总有一天会被触发,这可能导致严重的失败。

然而,不幸的是,测试只能揭示错误的存在,而不能揭示它们的缺失;尽管如此,良好和彻底的测试绝对至关重要。大多数进行的测试(编写的测试用例)往往是正面的测试用例;有趣的是,大多数软件(安全)漏洞都逃脱了这种测试的注意。负面测试用例有助于捕捉这些失败;一类名为fuzzing的软件测试在这方面大有帮助。在不同的机器架构上测试代码也可以帮助暴露隐藏的缺陷。

使用 Linux 内核的控制组

使用 Linux 内核的cgroups(控制组)技术来指定和限制资源分配和带宽。现代 Linux 系统上的 cgroup 控制器包括以下内容:CPU(限制 CPU 使用),CPU set(执行 CPU 亲和性的现代方式,将一组进程限制在一组 CPU 上),blkio(I/O 限制),devices(限制哪些进程可以使用哪些设备),freezer(暂停/恢复任务执行),memory(内存使用限制),net_cls(网络数据包标记 classid),net_prio(限制每个接口的网络流量),namespacesns),perf_event(性能分析)。

限制资源不仅在需求角度上至关重要,而且在安全角度上也是如此(想想恶意攻击者梦想中的[D]DoS 攻击)。顺便说一句,容器(基本上是一种轻量级虚拟化技术),现在是一个热门话题,主要是因为两个足够发展的 Linux 内核技术的结合:cgroups 和命名空间。

总结

问题:世界上最大的房间是什么?

答案:改进的空间!

总的来说,这应该总结出你在处理庞大项目时应该具有的态度,并且要终身学习诸如 Linux 之类的主题。我们再次敦促读者不仅要阅读以获得概念上的理解——这很重要!——还要动手编写代码。犯错误,修复错误,并从中学习。为开源项目做出贡献是一个绝佳的方式。