在 systemd 中使用控制组管理资源,


控制组可以按照应用管理资源,而不是按照组成应用的单个进程。

作为一个系统管理员,没有事情比意外地耗尽计算资源让我更觉得沮丧。我曾不止一次填满了一个分区的所有可用磁盘空间、耗尽内存、以及没有足够的 CPU 时间在合理的时间内处理我的任务。资源管理是系统管理员最重要的工作之一。

资源管理的关键是保证所有的进程能够相对公平的访问需要的系统资源。资源管理还包括确保在需要时添加内存、硬盘驱动器空间、还有 CPU 处理能力;或者在无法添加时限制资源的使用。此外,应该阻止独占系统资源的用户,无论其是否有意。

系统管理员可以通过一些工具监控和管理不同的系统资源。例如,top 和类似的工具允许你监控内存、I/O、存储(磁盘、SSD 等)、网络、交换空间、CPU 的用量等。这些工具,尤其是那些以 CPU 为中心的工具,大部分基于以运行的进程为基本单位进行控制的模型。它们最多只是提供了一种方式来调整 nice 数字,从而修改优先级,或者杀死一个运行的进程。(要了解 nice 数字的信息,查看 使用 Glances 监控 Linux 和 Windows 主机)。

SystemV 环境中基于传统的资源管理的其他工具,由 /etc/security/limits.conf 文件和 /etc/security/limits.d 中的本地配置文件控制。资源可以按照用户或组以一种相对粗糙但实用的方式限制。可以管理的资源包括内存的各个方面、每日的总 CPU 时间、数据总量、优先级、nice 数字、并发登录的数量、进程数、文件大小的最大值等。

使用控制组管理进程

systemd 和 SystemV 之间的一个主要差异是管理进程的方式。SystemV 将每个进程视作一个独立的实体。systemd 将相关的进程集中到一个控制组,简写做 cgroup,并将控制组作为一个整体管理系统资源。这意味着资源能够基于应用管理,而不是由组成应用的各个进程来管理。

控制组的控制单元称作切片单元slice unit。切片是允许 systemd 以树状格式控制程序次序,从而简化管理的概念化。

查看控制组

我将从一些允许你查看不同类型控制组信息的命令开始。 systemctl status <service> 命令显示一个特定服务的切片信息,包括服务的切片。这个例子展示了 at 守护进程:

  1. [root@testvm1 ~]# systemctl status atd.service
  2. ● atd.service - Deferred execution scheduler
  3. Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
  4. Active: active (running) since Wed 2020-09-23 12:18:24 EDT; 1 day 3h ago
  5. Docs: man:atd(8)
  6. Main PID: 1010 (atd)
  7. Tasks: 1 (limit: 14760)
  8. Memory: 440.0K
  9. CPU: 5ms
  10. CGroup: /system.slice/atd.service
  11. └─1010 /usr/sbin/atd -f
  12.  
  13. Sep 23 12:18:24 testvm1.both.org systemd[1]: Started Deferred execution scheduler.
  14. [root@testvm1 ~]#

这是一个我感到 systemd 比 SystemV 和旧的初始化程序更好用的原因的绝佳示例。这里的信息远比 SystemV 能够提供的丰富。CGroup 项包括的层级结构中,system.slice 是 systemd(PID 1),atd.service 在下一层,是 system.slice 的一部分。CGroup 项的第二行还显示了进程 ID(PID)和启动守护进程使用的命令。

systemctl 命令可以列出多个控制组项,--all 参数列出所有的切片,包括当前没有激活的切片:

  1. [root@testvm1 ~]# systemctl -t slice --all
  2. UNIT LOAD ACTIVE SUB DESCRIPTION
  3. -.slice loaded active active Root Slice
  4. system-getty.slice loaded active active system-getty.slice
  5. system-lvm2\x2dpvscan.slice loaded active active system-lvm2\x2dpvscan.slice
  6. system-modprobe.slice loaded active active system-modprobe.slice
  7. system-sshd\x2dkeygen.slice loaded active active system-sshd\x2dkeygen.slice
  8. system-systemd\x2dcoredump.slice loaded inactive dead system-systemd\x2dcoredump.slice
  9. system-systemd\x2dfsck.slice loaded active active system-systemd\x2dfsck.slice
  10. system.slice loaded active active System Slice
  11. user-0.slice loaded active active User Slice of UID 0
  12. user-1000.slice loaded active active User Slice of UID 1000
  13. user.slice loaded active active User and Session Slice
  14.  
  15. LOAD = Reflects whether the unit definition was properly loaded.
  16. ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
  17. SUB = The low-level unit activation state, values depend on unit type.
  18.  
  19. 11 loaded units listed.
  20. To show all installed unit files use 'systemctl list-unit-files'.
  21. [root@testvm1 ~]#

关于这个数据,第一个需要注意的是数据显示了 UID 0(root)和 UID 1000 的用户切片,UID 1000 是我登录的用户。这里列出了组成每个切片的切片部分,而不是服务。还说明了每个用户登录时都会为其创建一个切片,这为将一个用户的所有任务作为单个控制组项进行管理提供了一种方式。

探索控制组的层次结构

目前为止一切顺利,但是控制组是分层的,所有的服务单元作为其中一个控制组的成员运行。要查看这个层次结构很简单,使用一个旧命令和 systemd 的一个新命令即可。

ps 命令可以用于映射进程的和其所处的控制组层次。注意使用 ps 命令时需要指明想要的数据列。我大幅削减了下面命令的输出数量,但是试图保留足够的数据,以便你能够对自己系统上的输出有所感受:

  1. [root@testvm1 ~]# ps xawf -eo pid,user,cgroup,args
  2. PID USER CGROUP COMMAND
  3. 2 root - [kthreadd]
  4. 3 root - \_ [rcu_gp]
  5. 4 root - \_ [rcu_par_gp]
  6. 6 root - \_ [kworker/0:0H-kblockd]
  7. 9 root - \_ [mm_percpu_wq]
  8. 10 root - \_ [ksoftirqd/0]
  9. 11 root - \_ [rcu_sched]
  10. 12 root - \_ [migration/0]
  11. 13 root - \_ [cpuhp/0]
  12. 14 root - \_ [cpuhp/1]
  13. <删节>
  14. 625406 root - \_ [kworker/3:0-ata_sff]
  15. 625409 root - \_ [kworker/u8:0-events_unbound]
  16. 1 root 0::/init.scope /usr/lib/systemd/systemd --switched-root --system --deserialize 30
  17. 588 root 0::/system.slice/systemd-jo /usr/lib/systemd/systemd-journald
  18. 599 root 0::/system.slice/systemd-ud /usr/lib/systemd/systemd-udevd
  19. 741 root 0::/system.slice/auditd.ser /sbin/auditd
  20. 743 root 0::/system.slice/auditd.ser \_ /usr/sbin/sedispatch
  21. 764 root 0::/system.slice/ModemManag /usr/sbin/ModemManager
  22. 765 root 0::/system.slice/NetworkMan /usr/sbin/NetworkManager --no-daemon
  23. 767 root 0::/system.slice/irqbalance /usr/sbin/irqbalance --foreground
  24. 779 root 0::/system.slice/mcelog.ser /usr/sbin/mcelog --ignorenodev --daemon --foreground
  25. 781 root 0::/system.slice/rngd.servi /sbin/rngd -f
  26. 782 root 0::/system.slice/rsyslog.se /usr/sbin/rsyslogd -n
  27. <删节>
  28. 893 root 0::/system.slice/sshd.servi sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
  29. 1130 root 0::/user.slice/user-0.slice \_ sshd: root [priv]
  30. 1147 root 0::/user.slice/user-0.slice | \_ sshd: root@pts/0
  31. 1148 root 0::/user.slice/user-0.slice | \_ -bash
  32. 1321 root 0::/user.slice/user-0.slice | \_ screen
  33. 1322 root 0::/user.slice/user-0.slice | \_ SCREEN
  34. 1323 root 0::/user.slice/user-0.slice | \_ /bin/bash
  35. 498801 root 0::/user.slice/user-0.slice | | \_ man systemd.resource-control
  36. 498813 root 0::/user.slice/user-0.slice | | \_ less
  37. 1351 root 0::/user.slice/user-0.slice | \_ /bin/bash
  38. 123293 root 0::/user.slice/user-0.slice | | \_ man systemd.slice
  39. 123305 root 0::/user.slice/user-0.slice | | \_ less
  40. 1380 root 0::/user.slice/user-0.slice | \_ /bin/bash
  41. 625412 root 0::/user.slice/user-0.slice | | \_ ps xawf -eo pid,user,cgroup,args
  42. 625413 root 0::/user.slice/user-0.slice | | \_ less
  43. 246795 root 0::/user.slice/user-0.slice | \_ /bin/bash
  44. 625338 root 0::/user.slice/user-0.slice | \_ /usr/bin/mc -P /var/tmp/mc-root/mc.pwd.246795
  45. 625340 root 0::/user.slice/user-0.slice | \_ bash -rcfile .bashrc
  46. 1218 root 0::/user.slice/user-1000.sl \_ sshd: dboth [priv]
  47. 1233 dboth 0::/user.slice/user-1000.sl \_ sshd: dboth@pts/1
  48. 1235 dboth 0::/user.slice/user-1000.sl \_ -bash
  49. <删节>
  50. 1010 root 0::/system.slice/atd.servic /usr/sbin/atd -f
  51. 1011 root 0::/system.slice/crond.serv /usr/sbin/crond -n
  52. 1098 root 0::/system.slice/lxdm.servi /usr/sbin/lxdm-binary
  53. 1106 root 0::/system.slice/lxdm.servi \_ /usr/libexec/Xorg -background none :0 vt01 -nolisten tcp -novtswitch -auth /var/run/lxdm/lxdm-:0.auth
  54. 370621 root 0::/user.slice/user-1000.sl \_ /usr/libexec/lxdm-session
  55. 370631 dboth 0::/user.slice/user-1000.sl \_ xfce4-session
  56. 370841 dboth 0::/user.slice/user-1000.sl \_ /usr/bin/ssh-agent /bin/sh -c exec -l bash -c "/usr/bin/startxfce4"
  57. 370911 dboth 0::/user.slice/user-1000.sl \_ xfwm4 --display :0.0 --sm-client-id 2dead44ab-0b4d-4101-bca4-e6771f4a8ac2
  58. 370930 dboth 0::/user.slice/user-1000.sl \_ xfce4-panel --display :0.0 --sm-client-id 2ce38b8ef-86fd-4189-ace5-deec1d0e0952
  59. 370942 dboth 0::/user.slice/user-1000.sl | \_ /usr/lib64/xfce4/panel/wrapper-2.0 /usr/lib64/xfce4/panel/plugins/libsystray.so 6 23068680 systr
  60. ay Notification Area Area where notification icons appear
  61. 370943 dboth 0::/user.slice/user-1000.sl | \_ /usr/lib64/xfce4/panel/wrapper-2.0 /usr/lib64/xfce4/panel/plugins/libpulseaudio-plugin.so 8 2306
  62. 8681 pulseaudio PulseAudio Plugin Adjust the audio volume of the PulseAudio sound system
  63. 370944 dboth 0::/user.slice/user-1000.sl | \_ /usr/lib64/xfce4/panel/wrapper-2.0 /usr/lib64/xfce4/panel/plugins/libxfce4powermanager.so 9 2306
  64. 8682 power-manager-plugin Power Manager Plugin Display the battery levels of your devices and control the brightness of your display
  65. 370945 dboth 0::/user.slice/user-1000.sl | \_ /usr/lib64/xfce4/panel/wrapper-2.0 /usr/lib64/xfce4/panel/plugins/libnotification-plugin.so 10 2
  66. 3068683 notification-plugin Notification Plugin Notification plugin for the Xfce panel
  67. 370948 dboth 0::/user.slice/user-1000.sl | \_ /usr/lib64/xfce4/panel/wrapper-2.0 /usr/lib64/xfce4/panel/plugins/libactions.so 14 23068684 acti
  68. ons Action Buttons Log out, lock or other system actions
  69. 370934 dboth 0::/user.slice/user-1000.sl \_ Thunar --sm-client-id 2cfc809d8-4e1d-497a-a5c5-6e4fa509c3fb --daemon
  70. 370939 dboth 0::/user.slice/user-1000.sl \_ xfdesktop --display :0.0 --sm-client-id 299be0608-4dca-4055-b4d6-55ec6e73a324
  71. 370962 dboth 0::/user.slice/user-1000.sl \_ nm-applet
  72. <删节>

你可以使用 systemd-cgls 命令查看整个层次结构,这个命令不需要任何的复杂参数,更加简单。

我也大幅缩短了这个树状结构,但是保留了足够多的输出,以便你能够了解在自己的系统上执行这个命令时应该看到的数据总量和条目类型。我在我的一个虚拟机上执行了这个命令,输出大概有 200 行;我的主要工作站的输出大概有 250 行。

  1. [root@testvm1 ~]# systemd-cgls
  2. Control group /:
  3. -.slice
  4. ├─user.slice
  5. │ ├─user-0.slice
  6. │ │ ├─session-1.scope
  7. │ │ │ ├─ 1130 sshd: root [priv]
  8. │ │ │ ├─ 1147 sshd: root@pts/0
  9. │ │ │ ├─ 1148 -bash
  10. │ │ │ ├─ 1321 screen
  11. │ │ │ ├─ 1322 SCREEN
  12. │ │ │ ├─ 1323 /bin/bash
  13. │ │ │ ├─ 1351 /bin/bash
  14. │ │ │ ├─ 1380 /bin/bash
  15. │ │ │ ├─123293 man systemd.slice
  16. │ │ │ ├─123305 less
  17. │ │ │ ├─246795 /bin/bash
  18. │ │ │ ├─371371 man systemd-cgls
  19. │ │ │ ├─371383 less
  20. │ │ │ ├─371469 systemd-cgls
  21. │ │ │ └─371470 less
  22. │ │ └─user@0.service …
  23. │ │ ├─dbus-broker.service
  24. │ │ │ ├─1170 /usr/bin/dbus-broker-launch --scope user
  25. │ │ │ └─1171 dbus-broker --log 4 --controller 12 --machine-id 3bccd1140fca488187f8a1439c832f07 --max-bytes 100000000000000 --max-fds 25000000000000 --max->
  26. │ │ ├─gvfs-daemon.service
  27. │ │ │ └─1173 /usr/libexec/gvfsd
  28. │ │ └─init.scope
  29. │ │ ├─1137 /usr/lib/systemd/systemd --user
  30. │ │ └─1138 (sd-pam)
  31. │ └─user-1000.slice
  32. │ ├─user@1000.service …
  33. │ │ ├─dbus\x2d:1.2\x2dorg.xfce.Xfconf.slice
  34. │ │ │ └─dbus-:1.2-org.xfce.Xfconf@0.service
  35. │ │ │ └─370748 /usr/lib64/xfce4/xfconf/xfconfd
  36. │ │ ├─dbus\x2d:1.2\x2dca.desrt.dconf.slice
  37. │ │ │ └─dbus-:1.2-ca.desrt.dconf@0.service
  38. │ │ │ └─371262 /usr/libexec/dconf-service
  39. │ │ ├─dbus-broker.service
  40. │ │ │ ├─1260 /usr/bin/dbus-broker-launch --scope user
  41. │ │ │ └─1261 dbus-broker --log 4 --controller 11 --machine-id
  42. <删节>
  43. │ │ └─gvfs-mtp-volume-monitor.service
  44. │ │ └─370987 /usr/libexec/gvfs-mtp-volume-monitor
  45. │ ├─session-3.scope
  46. │ │ ├─1218 sshd: dboth [priv]
  47. │ │ ├─1233 sshd: dboth@pts/1
  48. │ │ └─1235 -bash
  49. │ └─session-7.scope
  50. │ ├─370621 /usr/libexec/lxdm-session
  51. │ ├─370631 xfce4-session
  52. │ ├─370805 /usr/bin/VBoxClient --clipboard
  53. │ ├─370806 /usr/bin/VBoxClient --clipboard
  54. │ ├─370817 /usr/bin/VBoxClient --seamless
  55. │ ├─370818 /usr/bin/VBoxClient --seamless
  56. │ ├─370824 /usr/bin/VBoxClient --draganddrop
  57. │ ├─370825 /usr/bin/VBoxClient --draganddrop
  58. │ ├─370841 /usr/bin/ssh-agent /bin/sh -c exec -l bash -c "/usr/bin/startxfce4"
  59. │ ├─370910 /bin/gpg-agent --sh --daemon --write-env-file /home/dboth/.cache/gpg-agent-info
  60. │ ├─370911 xfwm4 --display :0.0 --sm-client-id 2dead44ab-0b4d-4101-bca4-e6771f4a8ac2
  61. │ ├─370923 xfsettingsd --display :0.0 --sm-client-id 261b4a437-3029-461c-9551-68c2c42f4fef
  62. │ ├─370930 xfce4-panel --display :0.0 --sm-client-id 2ce38b8ef-86fd-4189-ace5-deec1d0e0952
  63. │ ├─370934 Thunar --sm-client-id 2cfc809d8-4e1d-497a-a5c5-6e4fa509c3fb --daemon
  64. │ ├─370939 xfdesktop --display :0.0 --sm-client-id 299be0608-4dca-4055-b4d6-55ec6e73a324
  65. <删节>
  66. └─system.slice
  67. ├─rngd.service
  68. │ └─1650 /sbin/rngd -f
  69. ├─irqbalance.service
  70. │ └─1631 /usr/sbin/irqbalance --foreground
  71. ├─fprintd.service
  72. │ └─303383 /usr/libexec/fprintd
  73. ├─systemd-udevd.service
  74. │ └─956 /usr/lib/systemd/systemd-udevd
  75. <删节>
  76. ├─systemd-journald.service
  77. │ └─588 /usr/lib/systemd/systemd-journald
  78. ├─atd.service
  79. │ └─1010 /usr/sbin/atd -f
  80. ├─system-dbus\x2d:1.10\x2dorg.freedesktop.problems.slice
  81. │ └─dbus-:1.10-org.freedesktop.problems@0.service
  82. │ └─371197 /usr/sbin/abrt-dbus -t133
  83. ├─sshd.service
  84. │ └─893 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
  85. ├─vboxservice.service
  86. │ └─802 /usr/sbin/VBoxService -f
  87. ├─crond.service
  88. │ └─1011 /usr/sbin/crond -n
  89. ├─NetworkManager.service
  90. │ └─765 /usr/sbin/NetworkManager --no-daemon
  91. ├─switcheroo-control.service
  92. │ └─787 /usr/libexec/switcheroo-control
  93. <删节>

这个树状视图显示了所有的用户和系统切片,以及每个控制组内正在运行的服务和程序。注意叫作 scope(范围)的单元,它将相关的程序组成一个管理单元,在上面列出的结果中就是 user-1000.sliceuser-1000.slice/session-7.scope 控制组包含了 GUI 桌面程序层次结构,以 LXDM 显示管理器会话和其所有的子任务开始,包括像 Bash 命令行解释器和 Thunar GUI 文件管理器之类的程序。

配置文件中不定义范围单元,而是作为启动相关程序组的结果程序化生成的。范围单元不创建或启动作为控制组的组成部分运行的进程。范围内的所有进程都是平等的,没有内部的层次结构。一个范围的生命周期在第一个进程创建时开始,在最后一个进程销毁时结束。

在你的桌面打开多个窗口,比如终端模拟器、LibreOffice、或者任何你想打开的,然后切换到一个可用的虚拟控制台,启动类似 top 或 Midnight Commander 的程序。在主机运行 systemd-cgls 命令,留意整体的层次结构和范围单元。

systemd-cgls 命令提供的控制组层次结构表示(以及组成控制组单元的细节),比我见过的其他任何指令都要完整。和 ps 命令提供的输出相比,我喜欢 systemd-cgls 命令更简洁的树形表示。

来自朋友们的一点帮助

介绍完这些基础知识后,我曾计划过深入研究控制组的更多细节,以及如何使用,但是我在 Opensource.com 的姐妹网站 Enable Sysadmin 上发现了一系列四篇优秀文章,由 Red Hat 公司的 Steve Ovens 所作。与其从头重写 Steve 的文章,我觉得倒不如通过链接到这些文章,利用他的控制组专业知识:

像我一样享受这些文章并从中汲取知识吧。

其他资源

互联网上充斥着大量关于 systemd 的信息,但大部分都简短生硬、愚钝、甚至令人误解。除了本文提到的资源,下面的网页提供了关于 systemd 启动更详细可靠的信息。自从我开始这一系列的文章来反映我所做的研究以来,这个的列表已经变长了。

  • Fedora 项目有一个优质实用的 systemd 指南,几乎有你使用 systemd 配置、管理、维护一个 Fedora 计算机需要知道的一切。
  • Fedora 项目还有一个好用的 速查表,交叉引用了古老的 SystemV 命令和对应的 systemd 命令。
  • systemd.unit(5) 手册页 包含了一个不错的单元文件中各个节的列表,以及这些节的配置选项和简洁的描述。
  • Red Hat 文档包含了一个 单元文件结构 的有用描述,还有一些其他的重要信息。
  • 要获取 systemd 的详细技术信息和创立的原因,查看 Freedesktop.org 的 systemd 描 述。这个使我发现过的最棒页面之一,因为其中包含了许多指向其他重要准确文档的链接。
  • Linux.com 上 “systemd 的更多乐趣” 提供了更高级的 systemd 信息和提示。
  • 查看 systemd.resource-control(5) 的手册页
  • 查看 Linux 内核用户和管理员指南 中的 控制组 v2 条目。

相关内容