systemd 是一个 Linux 系统基础组件的集合,提供了一个系统和服务管理器,运行为 PID 1 并负责启动其它程序。功能包括:支持并行化任务;同时采用 socket 式与 D-Bus 总线式激活服务;按需启动守护进程(daemon);利用 Linux 的 cgroups 监视进程;支持快照和系统恢复;维护挂载点和自动挂载点;各服务间基于依赖关系进行精密控制。systemd 支持 SysV 和 LSB 初始脚本,可以替代 sysvinit。除此之外,功能还包括日志进程控制基础系统配置维护登陆用户列表以及系统账户运行时目录和设置,可以运行容器和虚拟机,可以简单的管理网络配置、网络时间同步日志转发和名称解析等。

systemd目的是取代Unix时代以来一直使用的init系统,兼容SysV和LSB的启动脚本。
init系统有两个缺点:

  • 一是启动时间长。init进程是串行启动,只有前一个进程启动完,才会启动下一个进程。
  • 二是启动脚本复杂。init进程只是执行启动脚本,不管其他事情。脚本需要自己处理各种情况,这往往使得脚本变得很长。

使用systemd就不需要再用init了。systemd取代了initd成为系统第一个进程(PID等于1),其他进程都是它的子进程。

systemd特性:

  • 支持并行化任务;
  • 同时采用socket式与D-Bus总线式激活服务;
  • 按需启动守护进程(daemon);
  • 利用 Linux 的 cgroups 监视进程;
  • 支持快照和系统恢复;
  • 维护挂载点和自动挂载点;
  • 各服务间基于依赖关系进行精密控制。
1
2
3
4
5
6
7
8
9
$ systemctl --version #查看systemd的版本

$ whereis systemd #检查systemd的二进制文件和库文件的安装位置
systemd: /usr/lib/systemd /etc/systemd /usr/share/systemd /usr/share/man/man1/systemd.1.gz

$ whereis systemctl # 检查systemctl的二进制文件和库文件的安装位置
systemctl: /usr/bin/systemctl /usr/share/man/man1/systemctl.1.gz

$ ps -eaf | grep [s]ystemd #检查systemd是否运行

Systemd 的优点是功能强大,使用方便,缺点是体系庞大,非常复杂。事实上,现在还有很多人反对使用 Systemd,理由就是它过于复杂,与操作系统的其他部分强耦合,违反”keep simple, keep stupid”的Unix 哲学

Systemd 架构图

systemd可以管理所有系统资源。不同的资源统称为Unit(单元)。

Unit一共分为12种

  • 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:定时器

监视和控制systemd的主要命令是systemctl。该命令可用于查看系统状态和管理系统及服务。

使用systemctl控制单元时,通常需要使用单元文件的全名,包括扩展名(例如:sshd.service)。但是有些单元可以在systemctl中使用简写的方式。

  • 如果无扩展名,systemctl 默认把扩展名当作 .service 。例如 netcfg 和 netcfg.service 是等价的。
  • 挂载点会自动转化为相应的 .mount 单元。例如 /home 等价于 home.mount 。
  • 设备会自动转化为相应的 .device 单元,所以 /dev/sda2 等价于 dev-sda2.device 。
1
2
注意: 有一些单元的名称包含一个 @ 标记(例如: name@string.service ),这意味着它是模板单元 name@.service 的一个 实例。 string 被称作实例标识符,在 systemctl 调用模板单元时,会将其当作一个参数传给模板单元,模板单元会使用这个传入的参数代替模板中的 %I 指示符。
在实例化之前,systemd 会先检查 name@string.suffix 文件是否存在(如果存在,就直接使用这个文件,而不是模板实例化)。大多数情况下,包含 @ 标记都意味着这个文件是模板。如果一个模板单元没有实例化就调用,该调用会返回失败,因为模板单元中的 %I 指示符没有被替换。

系统管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 重启系统
$ sudo systemctl reboot

# 关闭系统,切断电源
$ sudo systemctl poweroff

# CPU停止工作
$ sudo systemctl halt

# 待机
$ sudo systemctl suspend

# 休眠
$ sudo systemctl hibernate

# 让系统进入交互式休眠状态
$ sudo systemctl hybrid-sleep

# 启动进入救援状态(单用户状态)
$ sudo systemctl rescue

systemd-analyze

systemd-analyze用于查看启动耗时。

1
2
3
4
5
6
7
8
9
10
11
# 查看启动耗时
$ systemd-analyze

# 查看每个服务的启动耗时
$ systemd-analyze blame

# 显示瀑布状的启动过程流
$ systemd-analyze critical-chain

# 显示指定服务的启动流
$ systemd-analyze critical-chain atd.service

hostnamectl

hostnamectl命令用于查看主机的信息

1
2
3
4
5
# 显示当前主机的信息
$ hostnamectl

# 设置主机名。
$ sudo hostnamectl set-hostname rhel7

localectl

localectl命令用于查看本地化设置。

1
2
3
4
5
6
# 查看本地化设置
$ localectl

# 设置本地化参数。
$ sudo localectl set-locale LANG=en_GB.utf8
$ sudo localectl set-keymap en_GB

timedatectl

timedatectl命令用于查看当前时区设置。

1
2
3
4
5
6
7
8
9
10
# 查看当前时区设置
$ timedatectl

# 显示所有可用的时区
$ timedatectl list-timezones

# 设置当前时区
$ sudo timedatectl set-timezone America/New_York
$ sudo timedatectl set-time YYYY-MM-DD
$ sudo timedatectl set-time HH:MM:SS

loginctl

loginctl命令用于查看当前登录的用户。

1
2
3
4
5
6
7
8
# 列出当前session
$ loginctl list-sessions

# 列出当前登录用户
$ loginctl list-users

# 列出显示指定用户的信息
$ loginctl show-user fynn

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
30
31
32
33
34
35
# 列出所有可用单元
$ systemctl list-unit-files

# 列出所有运行中单元
$ systemctl list-units

# 列出所有Unit,包括没有找到配置文件的或者启动失败的
$ systemctl list-units --all

# 列出所有没有运行的 Unit
$ systemctl list-units --all --state=inactive

# 列出所有失败单元
$ systemctl --failed

# 列出所有失败单元
$ systemctl list-units --failed

# 列出所有正在运行的、类型为 service 的 Unit
$ systemctl list-units --type=service

# 检查某个单元是否启动
$ systemctl is-enabled crond.service

# 检查某个单元或服务是否运行
$ systemctl status firewalld.service

# 显示远程主机的某个 Unit 的状态
$ systemctl -H root@rhel7.example.com status httpd.service

# 显示某个 Unit 是否正在运行
$ systemctl is-active application.service

# 显示某个 Unit 是否处于启动失败状态
$ systemctl is-failed application.service

Unit管理

下面命令是最长用到的一些命令

立即激活单元:

1
$ sudo systemctl start <单元>

立即停止单元:

1
$ sudo systemctl stop <单元>

杀死一个服务的所有子进程:

1
$ sudo systemctl kill <单元>

重启单元:

1
$ sudo systemctl restart <单元>

重新加载配置:

1
$ sudo systemctl reload <单元>

输出单元运行状态:

1
$ sudo systemctl status <单元>

检查单元是否配置为自动启动:

1
$ sudo systemctl is-enabled <单元>

开机激活单元:

1
$ sudo systemctl enable <单元>

设置单元为自动启动并立即启动这个单元:

1
$ sudo systemctl enable --now <单元>

取消开机自动激活单元:

1
$ sudo systemctl disable <单元> 

禁用一个单元(禁用后,间接启动也是不可能的):

1
$ sudo systemctl mask <单元>

取消禁用一个单元:

1
$ sudo systemctl unmask <单元>

显示单元的手册页(必须由单元文件提供):

1
$ sudo systemctl help <单元>

重新载入 systemd,扫描新的或有变动的单元:

1
$ sudo systemctl daemon-reload <单元>

显示某个Unit的所有底层参数

1
$ sudo systemctl show <单元>

显示某个 Unit 的指定属性的值

1
$ sudo  systemctl show -p CPUShares httpd.service

设置某个 Unit 的指定属性

1
sudo systemctl set-property httpd.service CPUShares=500

提示:

  • 上面的大部分命令可以跟多个单元名,详细参见 systemctl(1)
  • systemctl命令在enabledisablemask子命令中增加了--now选项,可以实现激活的同时启动服务,取消激活的同时停止服务。
  • 一个软件包可能会提供多个不同的单元。如果你已经安装了软件包,可以通过pacman -Qql package | grep systemd命令检查这个软件包提供了哪些单元。

依赖关系

Unit之间存在依赖关系:A依赖B,就意味着Systemd在启动A的时候,同时会去启动B。

systemctl list-dependencies 命令列出一个Unit的所有依赖 。

1
$ sudo systemctl list-dependencies nginx.service

上面命令的输出结果之中,有些依赖是 Target 类型(详见下文),默认不会展开显示。如果要展开 Target,就需要使用–all参数。

1
$ sudo systemctl list-dependencies --all nginx.service

target 目标

运行级别(runlevel)是一个旧的概念。现在,systemd 引入了一个和运行级别功能相似又不同的概念——目标(target)。不像数字表示的启动级别,每个目标都有名字和独特的功能,并且能同时启用多个。一些目标继承其他目标的服务,并启动新服务。systemd 提供了一些模仿 sysvinit 运行级别的目标,仍可以使用旧的 telinit 运行级别 命令切换。

运行级别:指的是Unix或者Linux等类Unix操作系统下不同的运行模式。

RunLevel是互斥的,不可能多个RunLevel同时启动,但是过个Target可以同时启动。简单说,Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候,Systemd 就会启动里面所有的 Unit。从这个意义上说,Target 这个概念类似于”状态点”,启动某个 Target 就好比启动到某种状态。

查看当前系统的所有 Target

1
$ systemctl list-unit-files --type=target

获取当前目标

1
systemctl list-units --type=target

查看一个Target 包含的多有Unit

1
$ systemctl list-dependencies multi-user.target

查看启动时的默认 Target

1
$ systemctl get-default

设置启动时的默认 Target

1
$ sudo systemctl set-default multi-user.target

切换 Target 时,默认不关闭前一个 Target 启动的进程,
systemctl isolate 命令改变这种行为,
关闭前一个 Target 里面所有不属于后一个 Target 的进程

1
$ sudo systemctl isolate multi-user.target

目标表

SysV 运行级别 Systemd 目标 注释
0 runlevel0.target, poweroff.target 中断系统(halt)
1, s, single runlevel1.target, rescue.target 单用户模式
2,4 runlevel2.target, runlevel4.target, multi-user.target 用户自定义运行级别,通常识别为级别3。
3 runlevel3.target, multi-user.target 多用户,无图形界面。用户可以通过终端或网络登录。
5 runlevel5.target, graphical.target 多用户,图形界面。继承级别3的服务,并启动图形界面服务。
6 runlevel6.target, reboot.target 重启
emergency emergency.target 急救模式(Emergency shell)

开机启动的目标时default.target,默认链接到graphical.target(大致相当于原来的运行级别5)。可以通过内核参数更改默认的运行级别:

  • systemd.unit=multi-user.target (大致相当于级别3)
  • systemd.unit=rescue.target (大致相当于级别1)

另一个方法是修改default.target是通过systemctl:

1
systemctl set-default multi-user.target

要覆盖已经设置的default.target请使用force:

1
systemctl set-default -f multi-user.target

日志管理

Systemd 统一管理所有 Unit 的启动日志。带来的好处就是,可以只用journalctl一个命令,查看所有日志(内核日志和应用日志)。日志的配置文件是/etc/systemd/journald.conf
journalctl功能强大,用法非常多。

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
72
73
74
75
76
77
78
79
80
# 查看所有日志(默认情况下 ,只保存本次启动的日志)
$ sudo journalctl

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

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

# 查看上一次启动的日志(需更改设置)
$ sudo journalctl -b -1

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

# 显示尾部的最新10行日志
$ sudo journalctl -n

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

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

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

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

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

# 查看指定用户的日志
$ sudo journalctl _UID=33 --since today

# 查看某个 Unit 的日志
$ sudo journalctl -u nginx.service
$ sudo journalctl -u nginx.service --since today

# 实时滚动显示某个 Unit 的最新日志
$ sudo journalctl -u nginx.service -f

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

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

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

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

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

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

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

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

参考

https://zh.wikipedia.org/wiki/Systemd
https://wiki.archlinux.org/index.php/systemd_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)
http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html
https://linux.cn/article-5926-1.html