分析Linux启动性能
使用 systemd-analyze 深入了解并解决 Linux 启动性能问题。
系统管理员的部分工作是分析系统性能,查找并解决导致性能不佳和启动时间过长的问题。系统管理员还需要检查 systemd 配置和使用的其他方面。
systemd init 系统提供了 systemd-analyze 工具,可以帮助发现性能问题和其他重要的 systemd 信息。在上一篇文章“分析 systemd 日历和时间跨度”中,我使用了 systemd-analyze
来分析 systemd 计时器中的时间戳和时间跨度,但该工具还有许多其他用途,其中一些用途我将在本文中进行探讨。
启动概述
Linux 启动序列是开始探索的好地方,因为许多 systemd-analyze 工具功能都是针对启动时的。但首先,了解引导和启动之间的区别很重要。引导顺序从 BIOS 开机自检 (POST) 开始,到内核完成加载并控制主机系统时结束,这是启动的开始,也是 systemd 日志开始的时间点。
在本系列的第二篇文章《了解 Linux 上启动时的 systemd》中,我更详细地讨论了启动过程中发生的情况和顺序。在本文中,我想检查启动顺序,以了解启动所需的时间以及哪些任务花费的时间最多。
下面我将显示的结果来自我的主工作站,这比虚拟机的结果有趣得多。该工作站由华硕 TUF X299 Mark 2 主板、16 核 32 个 CPU(线程)的 Intel i9-7960X CPU 和 64GB RAM 组成。下面的一些命令可以由非 root 用户运行,但我将在本文中使用 root 用户来避免在用户之间切换。
有多种选项可用于检查启动顺序。 systemd-analyze
命令的最简单形式显示启动、内核启动、加载和运行 initrd
等各个主要部分所花费的时间概览(即初始 ramdisk,一个临时系统映像,用于初始化某些硬件并安装 /
[root] 文件系统)和用户空间(其中将主机启动到所需的所有程序和守护进程)加载可用状态)。如果没有子命令传递给命令,则隐含 systemd-analyze time
:
[root@david ~]$ systemd-analyze
Startup finished in 53.921s (firmware) + 2.643s (loader) + 2.236s (kernel) + 4.348s (initrd) + 10.082s (userspace) = 1min 13.233s
graphical.target reached after 10.071s in userspace
[root@david ~]#
此输出中最值得注意的数据是固件 (BIOS) 所花费的时间:几乎 54 秒。这是非常长的时间,我的其他物理系统都没有花费如此长的时间来完成 BIOS。
我的 System76 Oryx Pro 笔记本电脑在 BIOS 中只花费了 8.506 秒,而我所有的自制系统只花费了不到 10 秒。经过一番网上搜索,我发现这款主板以其BIOS启动时间过长而闻名。我的主板从来不会“只是启动”。它总是挂起,我需要执行电源关闭/打开循环,然后 BIOS 启动时出现错误,我需要按 F1 进入 BIOS 配置,从中我可以选择启动驱动器并完成启动。这就是额外时间的来源。
并非所有主机都显示固件数据。我的不科学实验让我相信这些数据仅针对英特尔第 9 代或更高版本的处理器显示。但这可能是不正确的。
引导启动过程的概述很有趣,并且提供了很好的(尽管有限)信息,但是还有更多有关启动的信息,正如我将在下面描述的那样。
指责
您可以使用 systemd-analyzeblame 来发现哪些 systemd 单元初始化时间最长。结果按照初始化所需的时间从长到短的顺序显示:
[root@david ~]$ systemd-analyze blame
5.417s NetworkManager-wait-online.service
3.423s dracut-initqueue.service
2.715s systemd-udev-settle.service
2.519s fstrim.service
1.275s udisks2.service
1.271s smartd.service
996ms upower.service
637ms lvm2-monitor.service
533ms lvm2-pvscan@8:17.service
520ms dmraid-activation.service
460ms vboxdrv.service
396ms initrd-switch-root.service
<SNIP – removed lots of entries with increasingly small times>
由于其中许多服务是并行启动的,因此这些数字加起来可能会远远超过 BIOS 之后的所有服务的 systemd-analyze time 给出的总和。所有这些都是小数字,所以我在这里找不到任何显着的节省。
此命令中的数据可以指示您可能考虑使用哪些服务来缩短启动时间。不使用的服务可以被禁用。在此启动序列期间,似乎没有任何单个服务花费过长的时间。每次引导和启动您可能会看到不同的结果。
关键链
与项目管理中的关键路径一样,关键链显示了启动期间发生的时间关键的事件链。如果启动速度很慢,您需要查看这些 systemd 单元,因为它们会导致延迟。此工具不会显示所有启动的单元,仅显示此关键事件链中的单元:
[root@david ~]# systemd-analyze critical-chain
The time when unit became active or started is printed after the "@" character.
The time the unit took to start is printed after the "+" character.
graphical.target @10.071s
└─lxdm.service @10.071s
└─plymouth-quit.service @10.047s +22ms
└─systemd-user-sessions.service @10.031s +7ms
└─remote-fs.target @10.026s
└─remote-fs-pre.target @10.025s
└─nfs-client.target @4.636s
└─gssproxy.service @4.607s +28ms
└─network.target @4.604s
└─NetworkManager.service @4.383s +219ms
└─dbus-broker.service @4.434s +136ms
└─dbus.socket @4.369s
└─sysinit.target @4.354s
└─systemd-update-utmp.service @4.345s +9ms
└─auditd.service @4.301s +42ms
└─systemd-tmpfiles-setup.service @4.254s +42ms
└─import-state.service @4.233s +19ms
└─local-fs.target @4.229s
└─Virtual.mount @4.019s +209ms
└─systemd-fsck@dev-mapper-vg_david2\x2dVirtual.service @3.742s +274ms
└─local-fs-pre.target @3.726s
└─lvm2-monitor.service @356ms +637ms
└─dm-event.socket @319ms
└─-.mount
└─system.slice
└─-.slice
[root@david ~]#
前面带有 @
的数字显示自启动开始到设备处于活动状态时的绝对秒数。 +
前面的数字显示设备启动所需的时间。
系统状态
有时您需要确定系统的当前状态。 systemd-analyze dump 命令转储大量有关当前系统状态的数据。它以主启动时间戳列表、每个 systemd 单元的列表以及每个状态的完整描述开始:
[root@david ~]# systemd-analyze dump
Timestamp firmware: 1min 7.983523s
Timestamp loader: 3.872325s
Timestamp kernel: Wed 2020-08-26 12:33:35 EDT
Timestamp initrd: Wed 2020-08-26 12:33:38 EDT
Timestamp userspace: Wed 2020-08-26 12:33:42 EDT
Timestamp finish: Wed 2020-08-26 16:33:56 EDT
Timestamp security-start: Wed 2020-08-26 12:33:42 EDT
Timestamp security-finish: Wed 2020-08-26 12:33:42 EDT
Timestamp generators-start: Wed 2020-08-26 16:33:42 EDT
Timestamp generators-finish: Wed 2020-08-26 16:33:43 EDT
Timestamp units-load-start: Wed 2020-08-26 16:33:43 EDT
Timestamp units-load-finish: Wed 2020-08-26 16:33:43 EDT
Timestamp initrd-security-start: Wed 2020-08-26 12:33:38 EDT
Timestamp initrd-security-finish: Wed 2020-08-26 12:33:38 EDT
Timestamp initrd-generators-start: Wed 2020-08-26 12:33:38 EDT
Timestamp initrd-generators-finish: Wed 2020-08-26 12:33:38 EDT
Timestamp initrd-units-load-start: Wed 2020-08-26 12:33:38 EDT
Timestamp initrd-units-load-finish: Wed 2020-08-26 12:33:38 EDT
-> Unit system.slice:
Description: System Slice
Instance: n/a
Unit Load State: loaded
Unit Active State: active
State Change Timestamp: Wed 2020-08-26 12:33:38 EDT
Inactive Exit Timestamp: Wed 2020-08-26 12:33:38 EDT
Active Enter Timestamp: Wed 2020-08-26 12:33:38 EDT
Active Exit Timestamp: n/a
Inactive Enter Timestamp: n/a
May GC: no
<SNIP – Deleted a bazillion lines of output>
在我的主工作站上,此命令生成了 49,680 行、约 1.66MB 的流。该命令速度非常快,因此您无需等待结果。
我确实喜欢为各种连接设备提供的丰富细节,例如存储。每个 systemd 单元都有一个包含详细信息的部分,例如各种运行时、缓存和日志目录的模式、用于启动单元的命令行、进程 ID (PID)、启动时间戳以及内存和文件限制。
systemd-analyze 的手册页显示了 systemd-analyze --user dump 选项,该选项旨在显示有关用户管理器内部状态的信息。这对我来说失败了,互联网搜索表明它可能有问题。在 systemd 中,--user
实例用于管理和控制属于每个用户的进程层次结构的资源。每个用户的进程都是控制组的一部分,我将在以后的文章中介绍这一点。
分析图
大多数尖头老板(PHB)和许多优秀的经理都发现漂亮的图表比我通常喜欢的基于文本的系统性能数据更容易阅读和理解。但有时,即使我喜欢一个好的图表,systemd-analyze
也提供了在 SVG 矢量图形图表中显示启动数据的功能。
以下命令生成一个矢量图形文件,该文件显示引导和启动期间发生的事件。生成这个文件只需要几秒钟:
[root@david ~]# systemd-analyze plot > /tmp/bootup.svg
此命令创建一个 SVG,它是一个文本文件,定义了一系列图形矢量,应用程序(包括 Image Viewer、Ristretto、Okular、Eye of Mate、LibreOffice Draw 等)使用这些图形矢量来生成图形。这些应用程序处理 SVG 文件以创建图像。
我使用 LibreOffice Draw 来渲染图表。该图表很大,您需要大幅放大才能看清任何细节。这是其中的一小部分:
(大卫·博思,CC BY-SA 4.0)
启动顺序位于图中时间线上零 (0) 的左侧,启动顺序位于零的右侧。这一小部分显示了内核、initrd
以及 initrd
启动的进程。
该图一目了然地显示了启动时间、启动时间以及主要依赖项。关键路径以红色突出显示。
另一个生成图形输出的命令是 systemd-analyze dot。它生成 DOT 格式的文本依赖图描述。然后,生成的数据流通过 dot
实用程序进行管道传输,该实用程序是可用于从各种类型的数据生成矢量图形文件的程序系列的一部分。这些 SVG 文件也可以通过上面列出的工具进行处理。
首先,生成文件。这在我的主工作站上花费了近九分钟:
[root@david ~]# time systemd-analyze dot | dot -Tsvg > /tmp/test.svg
Color legend: black = Requires
dark blue = Requisite
dark grey = Wants
red = Conflicts
green = After
real 8m37.544s
user 8m35.375s
sys 0m0.070s
[root@david ~]#
我不会在这里重现输出,因为生成的图表几乎是意大利面条。但你应该尝试一下并查看结果才能明白我的意思。
条件句
我在阅读 systemd-analyze(1) 手册页时发现的更有趣但有些通用的功能之一是 condition
子命令。 (是的,我确实阅读了手册页,通过这种方式学到的东西真是令人惊奇!)这个 condition
子命令可用于测试可在 systemd 单元文件中使用的条件和断言。
它还可以在脚本中用于评估一个或多个条件 - 如果满足所有条件,则返回零 (0);如果不满足任何条件,则返回一 (1)。无论哪种情况,它都会喷出有关其发现的文字。
下面的示例来自手册页,有点复杂。它测试 4.0 和 5.1 之间的内核版本、主机是否在交流电源上运行、系统架构不是 ARM,以及目录 /etc/os-release
是否存在。我添加了 echo $?
语句来打印返回代码。
[root@david ~]# systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \
'ConditionKernelVersion = >=5.1' \
'ConditionACPower=|false' \
'ConditionArchitecture=|!arm' \
'AssertPathExists=/etc/os-release' ; \
echo $?
test.service: AssertPathExists=/etc/os-release succeeded.
Asserts succeeded.
test.service: ConditionArchitecture=|!arm succeeded.
test.service: ConditionACPower=|false failed.
test.service: ConditionKernelVersion=>=5.1 succeeded.
test.service: ConditionKernelVersion=!<4.0 succeeded.
Conditions succeeded.
0
[root@david ~]#
条件和断言列表从 systemd.unit(5)
手册页的第 600 行左右开始。
列出配置文件
systemd-analyze
工具提供了一种将各种配置文件的内容发送到 STDOUT
的方法,如下所示。基本目录是 /etc/
:
[root@david ~]# systemd-analyze cat-config systemd/system/display-manager.service
# /etc/systemd/system/display-manager.service
[Unit]
Description=LXDM (Lightweight X11 Display Manager)
#Documentation=man:lxdm(8)
Conflicts=getty@tty1.service
After=systemd-user-sessions.service getty@tty1.service plymouth-quit.service livesys-late.service
#Conflicts=plymouth-quit.service
[Service]
ExecStart=/usr/sbin/lxdm
Restart=always
IgnoreSIGPIPE=no
#BusName=org.freedesktop.lxdm
[Install]
Alias=display-manager.service
[root@david ~]#
与标准 cat
命令相比,输入的内容较多。我发现下一个命令有点帮助。它可以在标准 systemd 位置中搜索具有指定模式的文件:
[root@david ~]# systemctl cat backup*
# /etc/systemd/system/backup.timer
# This timer unit runs the local backup program
# (C) David Both
# Licensed under GPL V2
#
[Unit]
Description=Perform system backups
Requires=backup.service
[Timer]
Unit=backup.service
OnCalendar=*-*-* 00:15:30
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup.service
# This service unit runs the rsbu backup program
# By David Both
# Licensed under GPL V2
#
[Unit]
Description=Backup services using rsbu
Wants=backup.timer
[Service]
Type=oneshot
Environment="HOME=/root"
ExecStart=/usr/local/bin/rsbu -bvd1
ExecStart=/usr/local/bin/rsbu -buvd2
[Install]
WantedBy=multi-user.target
[root@david ~]#
这两个命令都在每个文件的内容前面添加了包含文件完整路径和名称的注释行。
单位文件验证
创建新的单元文件后,验证其语法是否正确会很有帮助。这就是 verify
子命令的作用。它可以列出拼写错误的指令并指出缺失的服务单元:
[root@david ~]# systemd-analyze verify /etc/systemd/system/backup.service
秉承“沉默是金”的 Unix/Linux 哲学,缺少输出消息意味着扫描的文件中没有错误。
安全
security
子命令检查指定服务的安全级别。它仅适用于服务单元,不适用于其他类型的单元文件:
[root@david ~]# systemd-analyze security display-manager
NAME DESCRIPTION >
✗ PrivateNetwork= Service has access to the host's network >
✗ User=/DynamicUser= Service runs as root user >
✗ CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP) Service may change UID/GID identities/capabilities >
✗ CapabilityBoundingSet=~CAP_SYS_ADMIN Service has administrator privileges >
✗ CapabilityBoundingSet=~CAP_SYS_PTRACE Service has ptrace() debugging abilities >
✗ RestrictAddressFamilies=~AF_(INET|INET6) Service may allocate Internet sockets >
✗ RestrictNamespaces=~CLONE_NEWUSER Service may create user namespaces >
✗ RestrictAddressFamilies=~… Service may allocate exotic sockets >
✗ CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP) Service may change file ownership/access mode/capabilities unres>
✗ CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER) Service may override UNIX file/IPC permission checks >
✗ CapabilityBoundingSet=~CAP_NET_ADMIN Service has network configuration privileges >
✗ CapabilityBoundingSet=~CAP_SYS_MODULE Service may load kernel modules
<SNIP>
✗ CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG Service may issue vhangup() >
✗ CapabilityBoundingSet=~CAP_WAKE_ALARM Service may program timers that wake up the system >
✗ RestrictAddressFamilies=~AF_UNIX Service may allocate local sockets >
→ Overall exposure level for backup.service: 9.6 UNSAFE ?
lines 34-81/81 (END)
是的,表情符号是输出的一部分。但是,当然,许多服务需要对所有内容进行几乎完全的访问才能完成其工作。我针对多个服务运行了这个程序,包括我自己的备份服务;结果可能有所不同,但底线似乎基本相同。
该工具对于检查和修复安全关键环境中的用户空间服务单元非常有用。我认为它对我们大多数人来说并没有什么好处。
最后的想法
这个强大的工具提供了一些有趣且非常有用的选项。本文探讨的大部分内容是关于使用 systemd-analyze 来深入了解使用 systemd 的 Linux 启动性能。它还可以分析systemd的其他方面。
其中一些工具的用途有限,还有一些应该完全忘记。但在解决启动和其他 systemd 功能的问题时,大多数都可以起到很好的效果。
资源
互联网上有大量有关 systemd 的信息,但很多信息都很简洁、晦涩,甚至具有误导性。除了本文提到的资源之外,以下网页还提供了有关 systemd 启动的更详细、更可靠的信息。 自从我开始写这一系列文章以反映我所做的研究以来,这个列表一直在增长。
- systemd.unit(5) 手册页包含一个很好的单元文件部分及其配置选项的列表,以及每个部分的简洁描述。
- Fedora 项目有一个很好的、实用的 systemd 指南。它几乎包含了使用 systemd 配置、管理和维护 Fedora 计算机所需了解的所有内容。
- Fedora 项目还有一个很好的备忘单,将旧的 SystemV 命令与类似的 systemd 命令交叉引用。
- Red Hat 文档包含对 Unit 文件结构的详细描述以及其他重要信息。
- 有关 systemd 的详细技术信息以及创建它的原因,请查看 Freedesktop.org 的 systemd 描述。
- Linux.com 的“More systemd fun”提供了更高级的 systemd 信息和技巧。
还有一系列由 Systemd 的设计者和主要开发人员 Lennart Poettering 撰写的针对 Linux 系统管理员的深入技术文章。这些文章是在 2010 年 4 月至 2011 年 9 月之间撰写的,但它们现在和当时一样具有相关性。关于 systemd 及其生态系统的许多其他优秀文章都基于这些论文。
- 重新思考 PID 1
- 面向管理员的 systemd,第 I 部分
- 面向管理员的 systemd,第二部分
- 面向管理员的 systemd,第 III 部分
- 管理员的 systemd,第四部分
- 面向管理员的 systemd,第五部分
- 面向管理员的 systemd,第六部分
- 面向管理员的 systemd,第七部分
- 面向管理员的 systemd,第八部分
- 面向管理员的 systemd,第 IX 部分
- 面向管理员的 systemd,第 X 部分
- 面向管理员的 systemd,第 XI 部分