0%

systemd

systemd 是 Linux 的系统和服务管理器。作为系统启动的第一个进程,初始化系统并且管理其他用户服务。

概述

systemd 提供了一套完整的系统启动和管理的解决方案,其中的 d 为守护进程(daemon)的缩写,即系统的守护进程。

systemd架构图

查看 systemd 简介

1
man systemd

systemd 作为系统的第一个进程,PID = 1,其余所有进程都是 systemd 的子进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@ubuntu:~# pstree -p
systemd(1)─┬─ModemManager(855)─┬─{ModemManager}(887)
│ └─{ModemManager}(894)
├─NetworkManager(777)─┬─{NetworkManager}(854)
│ └─{NetworkManager}(859)
├─VGAuthService(747)
├─accounts-daemon(766)─┬─{accounts-daemon}(778)
│ └─{accounts-daemon}(811)
├─acpid(767)
├─avahi-daemon(770)───avahi-daemon(840)
├─bluetoothd(771)
├─colord(1484)─┬─{colord}(1486)
│ └─{colord}(1488)
├─cron(773)
.....

Unit

systemd 可以管理所有系统资源,unit 为 systemd 的基本管理单元,用于描述系统服务、资源、设备和其他对象,根据不同资源分为了 12 种 Unit。

  • Service unit:系统服务
  • Target unit:多个 Unit 构成的一个组
  • Device Unit:硬件设备
  • Mount Unit:文件系统的挂载点
  • Automount Unit:自动挂载点
  • Path Unit:文件或路径
  • Scope Unit:不是由 Systemd 启动的外部进程
  • Slice Unit:进程组
  • Snapshot Unit:Systemd 快照,可以切回某个快照
  • Socket Unit:进程间通信的 socket
  • Swap Unit:swap 文件
  • Timer Unit:定时器

可通过 man systemd.service 查看各种 Unit 的简介。

配置

每个 Unit 需设置对应类型的配置文件。Unit 配置分为三个区块:Unit、Service\Timer\Socket、Install

Unit 区块

Unit 的元数据,定义了 unit 的基本信息、依赖关系和启动顺序。

1
2
3
4
5
6
7
[Unit]
Description=服务或单元的描述
Documentation=man:your-man-page(1) # 可选,文档链接
Requires=其他-unit.service # 可选,定义强依赖
Wants=其他-unit.service # 可选,定义弱依赖
After=network.target # 可选,指定启动顺序
Before=其他-unit.service # 可选,指定启动顺序
  • Description:简短描述
  • Documentation:文档地址,可以是手册页或 URL

依赖关系

例如:unit-A Requires/BindsTo/Wants/PartOf=unit-B.service

  • Requires
    • 强依赖
    • unit-A 启动时,自动启动被依赖 unit-B,如果被依赖 unit-B 启动失败,则该 unit-A 也会启动失败
  • BindsTo
    • 强依赖
    • unit-A 启动时,自动启动被依赖 unit-B,如果被依赖 unit-B 启动失败,则该 unit-A 也会启动失败
    • unit-B 停止或者失败时,依赖 unit-B 的 unit-A 也会自动停止
  • Wants
    • 弱依赖
    • unit-A 启动时,自动启动被依赖 unit-B,但被依赖 unit-B 启动失败,也不会一些该 unit-A 启动
特性 Requires BindsTo Wants
启动行为 必须成功启动被依赖的 unit 必须成功启动被依赖的 unit 建议启动被依赖的 unit
停止行为 不会自动停止依赖它的 unit 自动停止依赖它的 unit 不会自动停止依赖它的 unit
依赖关系类型 单向强依赖 双向强依赖 弱依赖
适用场景 功能密切相关但生命周期不必一致的服务 需要同时运行并在同一生命周期内运行的服务 可能相互关联但非必需的服务
  • Conflicts:互斥关系,unit-A 和 unit-B 不能同时运行,当 unit-A 启动时,会自动停止 unit-B,反之亦然
  • PartOf:unit-A 是目标服务 unit-B 的一部分,当目标服务 unit-B 停止或被重启时,作为其一部分的 unit-A 也会停止或重启

启动顺序

例如:unit-A Before/After=unit-B.service

  • Before:当前 unit A 应该在指定的 unit B 之前启动(A -> B)
  • After:当前 unit A 应该在指定的 unit B 之后启动(B -> A)

顺序配置BeforeAfter 只是设置一起启动时的顺序,A 服务启动时并不会自动启动 B 服务,如需设置自启动,需搭配依赖关系使用。

其他关系

  • Condition...:检查服务启动时的条件,如果条件不满足,服务将不会被启动,但不会导致启动失败
    • ConditionPathExists=:检查指定的文件或路径是否存在。如果路径存在,服务将启动
    • ConditionPathExistsGlob=:使用通配符检查路径是否匹配
    • ConditionPathIsDirectory=:检查指定路径是否是一个目录
    • ConditionPathIsSymbolicLink=:检查指定路径是否是一个符号链接
    • ConditionKernelCommandLine=:检查内核命令行参数是否包含指定的字符串
    • ConditionVirtualization=:检查当前系统是否运行在虚拟化环境中(如 KVM、LXC 等)
    • ConditionHost=:检查主机名是否与指定值匹配
  • Assert...:对服务启动的条件进行强制检查。与 Condition 不同,如果指定的条件未满足,服务将不会启动,且系统会报告错误
    • AssertPathExists=:检查指定路径是否存在
    • AssertPathIsDirectory=:检查指定路径是否是目录
    • AssertPathIsSymbolicLink=:检查指定路径是否是符号链接
    • AssertKernelCommandLine=:检查内核命令行参数
    • AssertVirtualization=:检查当前系统是否在虚拟化环境中

类型区块

Service

systemd.service 是用于定义和管理系统服务的单位类型。

配置文件以 .service 结尾,主要用于描述如何启动、停止和管理服务。

.service 配置中,通过 [Service] 区块定义服务的启动和运行参数。

1
2
3
4
5
6
7
8
9
10
11
12
[Service]
Type=notify
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755

启动类型

  • Type:配置服务如何通知systemd服务启动完成
    • simple(默认值):systemd 调用完 ExecStart 后即认为服务启动成功,即使 ExecStart 运行失败,服务也启动成功
    • notify:启动时,服务通过 sd_notify() 发送一个 “READY=1” 信号,systemd 收到这个信号后才认为服务启动成功,再启动之后的 unit
    • dbus:需设置 BusName=(如设置,自动为 dbus 类型),当 systemd 确认 BusName= 的 dbus 存在后,则认为服务启动成功
    • 等等

启动命令

  • EnvironmentFile:启动时会加载这个配置的环境变量文件

  • ExecStartPre:启动服务之前执行的命令

    • 如果 ExecStartPre 执行失败,则服务启动失败,不会执行 ExecStart

    • 如果希望 ExecStartPre 执行失败也能继续执行 ExecStart,需在 ExecStartPre 加前缀 -

      1
      ExecStartPre=-/usr/bin/check_dependencies
    • 支持多个 ExecStartPre,按顺序执行

  • ExecStart(必选):启动服务执行的命令

  • ExecStartPost:启动服务成功之后执行的命令

    • 如果 Type 规定的启动没成功,则不会执行 ExecStartPost

终止类型

  • KillMode:停止服务时如何处理进程
    • control-group(默认值):停止服务时,所有属于该服务 cgroup 的所有进程都会被杀死
    • process:停止服务时,只杀主进程,主进程产生的子进程不会被杀死
    • mixed:停止服务时,向主进程发送 SIGTERM 信号,接着向子进程发送 SIGKILL 信号
    • none:没有进程会被杀掉,只是执行服务的 ExecStop 命令

杀进程过程:

  1. 向进程发送KillSignal= 信号(默认为 SIGTERM
  2. 等待进程被杀死
  3. 若等候 TimeoutStopSec= 时间(默认 90s)后,进程仍然未被杀死,向进程发送 FinalKillSignal= 信号(默认为 SIGKILL)强制杀死进程

终止命令

  • ExecStop字段:停止服务时执行的命令
  • ExecStopPost字段:停止服务之后执行的命令

重启类型

有时候,服务需要保持运行状态,如有意外奔溃,能自动拉起,可通过下面配置设置自拉起

1
2
3
Restart=always
RestartSec=5
StartLimitInterval=0
  • Restart:重启机制,什么情况下会自动触发重启服务(systemctl stop/restart 触发的停止服务,不会被这里设置的重启机制拉起)
    • no(默认值):退出后不会重启
    • always:所有情况触发自动重启
    • on-success:只有正常退出时触发自动重启
    • on-failure:异常退出时触发自动重启
    • on-abnormal:只有被信号终止和超时,才会重启
    • on-abort:只有在收到没有捕捉到的信号终止时,才会重启
    • on-watchdog:超时退出,才会重启
退出原因 no always on-success on-failure on-abnormal on-abort on-watchdog
systemctl stop
正常退出 restart restart
退出码不为“0” restart restart
进程被强制杀死 restart restart restart restart
systemd操作超时 restart restart restart
看门狗超时 restart restart restart restart
  • 正常退出:退出码为”0”, 或者进程收到 SIGHUP, SIGINT, SIGTERM, SIGPIPE 信号之一,并且退出码符合 SuccessExitStatus= 的设置
  • 异常退出:包括退出码不为“0”、进程被强制杀死、systemd操作超时、看门狗超时
  • RestartSec:配合 Restart 使用,重新启动服务之前的睡眠时间,设置如异常退出后,等待多少秒再次启动,默认 100ms
1
2
3
                     |----RestartSec------|
---------|-----------|--------------------|------------|----------
running stopping stopped starting running
  • StartLimitIntervalStartLimitBurst:重启限制次数,在 StartLimitBurst= 时间内尝试重启超过 StartLimitInterval= 次则不再重启,默认为 10s 内 5 次,如需无限制,则设置 StartLimitInterval=0

重启命令

  • ExecReload字段:重启服务时执行的命令

Install 区块

定义服务单元的安装和启用选项,指定如何将服务单元与目标(如启动目标或其他服务)关联。

1
2
[Install]
WantedBy=multi-user.target
  • WantedBy:定义服务应该被哪些目标“需要”,这意味着在这些目标启动时,该服务也会被自动启动

    • 示例说明服务所在 target 是 multi-user.target,这个设置很通用且重要

    • 在 enable 这个服务后,在 /etc/systemd/system/multi-user.target.wants/ 的指定目录中创建了一个指向 /usr/lib/systemd/system/XXX.service 的符号链接

      1
      2
      root@ubuntu:~# systemctl enable nginx
      Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.
    • 相当于为 multi-user.target 添加了 Wants=nginx 配置

    • 当系统启动时, multi-user.target 启动,nginx.service 单元也会被启动

  • RequiredBy:类似于 WantedBy,但表示更强的依赖关系,如果目标启动失败,相关服务也不会启动

  • Alias:当前 Unit 的别名

  • Also:当前 Unit 启用(enable)或禁用(disable)时,会被同时(enable)或禁用(disable)的其他 Unit

配置路径

配置文件存放在下面地址:

  • 系统级目录:/lib/systemd/system/
    • Linux 系统提供的默认 unit 配置文件的存放位置
  • 用户级目录:~/.config/systemd/user/
    • 用户级的 unit 配置文件,允许普通用户定义和管理自己的服务和任务。
  • 本地配置目录:/etc/systemd/system/
    • 本地的 unit 配置文件,用户可以在这里创建或修改 unit 文件。

优先级顺序:

  1. /etc/systemd/system/(本地配置目录,最高优先级)
  2. /run/systemd/system/(临时配置,通常在运行时创建)
  3. /lib/systemd/system/(默认系统提供的配置)

优先级从高到低,如在高低优先级的目录下有同名 unit,则采用高优先级中的配置。

命令

systemd 由一组系统命令组成,用来控制系统的方方面面。

systemctl

参考链接:manpagesman systemctl

systemctl 是 systemd 的主命令,用于控制 systemd 系统与服务。

  • 命令格式:systemctl [OPTIONS...] COMMAND [UNIT...]

系统命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 列出 systemd 所有单位
systemctl list-units
# 通过 --type= 筛选出 unit 类型
systemctl list-units --type=service
# 通过 --state= 筛选出 unit 类型
systemctl list-units --state=running

# 列出 systemd 所有单元和状态
systemctl list-unit-files

# 列出已加载的套接字(socket)单元
systemctl list-sockets

# 列出已加载的定时器(timer)单元
systemctl list-timers

# 重新加载服务配置
systemctl daemon-reload

单 Unit 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 查看服务状态
systemctl status [SERVICE]

# 启动服务
systemctl start [SERVICE]

# 停止服务
systemctl stop [SERVICE]

# 重启服务
systemctl restart [SERVICE]

# 开机自启动服务
systemctl enable [SERVICE]

# 开机不自启动服务
systemctl disable [SERVICE]

# 显示服务信息
systemctl show [SERVICE]

# 读服务配置文件,等同于 cat 配置文件地址
systemctl cat [SERVICE]

# 编辑服务配置文件,相比直接 vim,编辑后可自动检查格式
systemctl edit [SERVICE]

# 显示指定 unit 的依赖关系
systemctl list-dependencies [UNIT]

journalctl

参考链接:manpagesman journalctl

journalctl 用来查看 systemd 日志,包括系统日志还有各 unit 日志。

  • 服务:systemd-journald.service
  • 命令格式:journalctl [OPTIONS...] [MATCHES...]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 查看所有日志(默认情况下 ,只保存本次启动的日志)
journalctl

# 显示尾部指定行数的日志
journalctl -n 20

# 实时滚动显示最新日志
journalctl -f

# 查看内核日志(不显示应用日志)
journalctl -k

# 查看系统本次启动的日志
journalctl -b

# 查看上一次启动的日志
journalctl -b -1

# 查看指定时间的日志
journalctl --since="2012-10-30 18:17:16"
journalctl --since "20 min ago"
journalctl --since yesterday
journalctl --since "2015-01-10" --until "2015-01-11 03:00"
journalctl --since 09:00 --until "1 hour ago"

# 查看指定服务的日志
journalctl /usr/lib/systemd/systemd

# 查看某个 Unit 的日志
journalctl -u nginx.service

# 合并显示多个 Unit 的日志
journalctl -u nginx.service -u php-fpm.service --since today

# 查看指定进程的日志
journalctl _PID=1

# 查看某个路径的脚本的日志
journalctl /usr/bin/bash

# 查看指定用户的日志
journalctl _UID=33

# 查看指定优先级(及其以上级别)的日志,共有8级
# 0: emerg
# 1: alert
# 2: crit
# 3: err
# 4: warning
# 5: notice
# 6: info
# 7: debug
journalctl -p err -b

# 日志默认分页输出,--no-pager 改为正常的标准输出
journalctl --no-pager

# 以 JSON 格式(单行)输出
journalctl -b -u nginx.service -o json

# 以 JSON 格式(多行)输出,可读性更好
journalctl -b -u nginx.service -o json-pretty

# 显示日志占据的硬盘空间
journalctl --disk-usage

# 指定日志文件占据的最大空间
journalctl --vacuum-size=1G

# 指定日志文件保存多久
journalctl --vacuum-time=1years

loginctl

参考链接:manpagesman loginctl

loginctl命令用来控制 systemd 登录管理器(systemd-logind.service)。

  • 服务:systemd-logind.service
  • 命令格式:loginctl [OPTIONS...] {COMMAND} [NAME...]

COMMAND 分为三种类型:用户、会话和席位

用户

用户是操作系统中进行交互和操作的实体。在Linux中,每个用户都有一个唯一的用户名和用户ID(UID)。用户可以是普通用户、超级用户(root)等,拥有不同的权限。

会话

会话是用户与系统之间的交互过程。当用户登录到Linux系统时,会创建一个会话。会话包含用户的环境设置、运行的程序、打开的文件等。一个用户可以同时拥有多个会话,例如在不同的终端或远程登录时。

席位

席位(TTY或终端)是用户与系统之间交互的接口。在Linux中,每个物理或虚拟终端都被称为一个席位。用户可以通过这些席位输入命令并接收输出。现代Linux系统通常支持多个席位,例如通过虚拟终端(tty)或图形界面(X Window System)。

  • 用户命令

    • 列出当前登录用户 list-users

      1
      2
      3
      4
      5
      6
      root@ubuntu:~# loginctl list-users
      UID USER
      0 root
      1000 guoyi

      2 users listed.
    • 显示用户状态user-status [USER...]

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      root@ubuntu:~# loginctl user-status root
      root (0)
      Since: Sat 2024-10-05 01:40:47 PDT; 9h ago
      State: active
      Sessions: 321 *304
      Linger: no
      Unit: user-0.slice
      ├─session-304.scope
      │ ├─57001 sshd: root@pts/1
      │ ├─57094 -bash
      │ ├─58718 loginctl user-status root
      │ └─58719 pager
      ├─session-321.scope
      │ ├─58486 sshd: root@pts/2
      │ └─58572 -bash
      └─user@0.service
      └─init.scope
      ├─57004 /lib/systemd/systemd --user
      └─57005 (sd-pam)

      Oct 05 01:40:48 ubuntu systemd[57004]: Condition check resulted in Sound System being sk>
      Oct 05 01:40:48 ubuntu systemd[57004]: Listening on REST API socket for snapd user sessi>
      Oct 05 01:40:48 ubuntu systemd[57004]: Listening on D-Bus User Message Bus Socket.
      Oct 05 01:40:48 ubuntu systemd[57004]: Reached target Sockets.
      Oct 05 01:40:48 ubuntu systemd[57004]: Reached target Basic System.
      Oct 05 01:40:48 ubuntu systemd[57004]: Condition check resulted in Sound Service being s
    • 显示用户各属性show-user [USER...],可以用 --all 选项查看缺省空置,使用 --property= 查看特定属性

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      root@ubuntu:~# loginctl show-user root
      UID=0
      GID=0
      Name=root
      Timestamp=Sat 2024-10-05 01:40:47 PDT
      TimestampMonotonic=574471117888
      RuntimePath=/run/user/0
      Service=user@0.service
      Slice=user-0.slice
      Display=304
      State=active
      Sessions=321 304
      IdleHint=no
      IdleSinceHint=0
      IdleSinceHintMonotonic=0
      Linger=no
    • 等等

  • 会话命令

    • 列出当前所有会话 list-sessions

      1
      2
      3
      4
      5
      6
      7
      root@ubuntu:~# loginctl list-sessions
      SESSION UID USER SEAT TTY
      2 1000 guoyi seat0 tty2
      304 0 root
      321 0 root

      3 sessions listed.
    • 显示简洁的会话状态信息session-status [ID...]

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      root@ubuntu:~# loginctl session-status 304
      304 - root (0)
      Since: Sat 2024-10-05 01:40:47 PDT; 9h ago
      Leader: 57001 (sshd)
      Remote: 192.168.45.1
      Service: sshd; type tty; class user
      State: active
      Unit: session-304.scope
      ├─57001 sshd: root@pts/1
      ├─57094 -bash
      ├─58744 loginctl session-status 304
      └─58745 pager

      Oct 05 01:40:48 ubuntu systemd[1]: Started Session 304 of user root.
    • 显示会话的各项属性值show-session [ID...]

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      root@ubuntu:~# loginctl show-session 321
      Id=321
      User=0
      Name=root
      Timestamp=Sat 2024-10-05 10:31:06 PDT
      TimestampMonotonic=606290337700
      VTNr=0
      Remote=yes
      RemoteHost=192.168.45.1
      Service=sshd
      Scope=session-321.scope
      Leader=58486
      Audit=321
      Type=tty
      Class=user
      Active=yes
      State=active
      IdleHint=no
      IdleSinceHint=0
      IdleSinceHintMonotonic=0
      LockedHint=no
    • 等等

  • 席位命令

    • 显示所有可用席位list-seats

      1
      2
      3
      4
      5
      root@ubuntu:~# loginctl list-seats
      SEAT
      seat0

      1 seats listed.
    • 等等

参考