一、systemd介绍
systemd是一个Linux系统基础组件的集合,提供了一个系统和服务管理器,运行为PID 1并负责启动其它程序。systemd采用并行化任务,可以提高系统的启动速度(SysV-init 采用串行启动)。除此之外,systemd还具有日志进程、控制基础系统配置、维护登陆用户列表以及系统账户、运行时目录和设置等功能。其次,systemd可以运行容器和虚拟机,管理网络配置、网络时间同步、日志转发和名称解析。
systemd的架构如下图:
二、Systemd VS Sysvinit
systemd相对于sysvinit的优点:
1、启动速度快:Systemd的目标是尽可能启动更少的进程,并将更多进程并行启动,以提高系统的启动速度。
2、按需启动:Systemd提供按需启动能力,可以按需加载服务,而SysVinit在系统初始化时会将所有可能用到的后台服务进程全部启动运行。
3、强大的管理能力:Systemd使用socket和D-Bus来开启服务,提供基于守护进程的按需启动策略,并保留了Linux cgroups的进程追踪功能。这些特性使得Systemd具有更强大的管理能力。
4、并行性能:Systemd支持快照和系统状态恢复,维护挂载和自挂载点,实现了各服务间基于从属关系的一个更为精细的逻辑控制,具有前卫的并行性能。
5、日志管理:Systemd的日志系统使用journalctl命令来查看日志信息,使得日志的检索和查看变得非常容易。Systemd的日志系统将所有可记录的事件保存在同一个数据存储中,从而使得日志内容的全局上下文得以保存并可供日后查询。
常见操作对比:
描述 | SysVinit | Systemd |
启动服务 | service example start | systemctl start example |
停止服务 | service example stop | systemctl stop example |
重新启动服务 | service example restart | systemctl restart example |
重新加载服务配置文件 | service example reload | systemctl reload example |
查看服务状态 | service example status | systemctl status example |
系统启动时启用服务 | chkconfig example on | systemctl enable example |
系统启动时禁用服务 | chkconfig example off | systemctl disable example |
打印服务列表 | chkconfig --list | systemctl list-unit-files --type=service |
systemd和sysvinit 运行级别对比:
Description | SysVInit | Systemd |
关机模式 | runlevel 0 | poweroff.target |
单用户root模式 | runlevel 1 | rescure.target |
离线多用户模式 | runlevel 2 | multi-user.target |
标准多用户模式 | runlevel 3 | multi-user.target |
安全模式 | runlevel 4 | multi-user.target |
带图形界面的多用户模式 | runlevel 5 | graphical.target |
重启模式 | runlevel 6 | reboot.target |
如何在yocto中修改systemd的运行模式:
SYSTEMD_DEFAULT_TARGET = "custom.target"
三、systemd命令使用
systemd提供了一些常用的命令,例如:
systemctl:这是systemd的主命令,用于管理系统。可以用来重启系统、关闭系统、停止CPU工作、暂停系统、让系统进入冬眠状态、让系统进入交互式休眠状态、启动进入救援状态(单用户状态)等。
systemd-analyze:这个命令用于查看启动耗时。
systemctl list-unit-files:可以列出所有可用单元(服务)。
systemctl list-units:可以列出所有运行中的单元。
systemctl list-unit-files | grep enable:可以查看自启动的软件。
systemctl daemon-reload:修改了某个单元的配置文件后,可以用这个命令重载配置文件。
systemctl reload mysqld.service:可以用这个命令重载某个单元。
hostnamectl:用于查看当前主机的信息。
localectl:用于查看本地化设置。
timedatectl:用于查看当前时区设置
3.1 systemctl使用
# 重启系统
$ sudo systemctl reboot
# 关闭系统,切断电源
$ sudo systemctl poweroff
# CPU停止工作
$ sudo systemctl halt
# 暂停系统
$ sudo systemctl suspend
# 让系统进入冬眠状态
$ sudo systemctl hibernate
# 让系统进入交互式休眠状态
$ sudo systemctl hybrid-sleep
# 启动进入救援状态(单用户状态)
$ sudo systemctl rescue
3.2 systemd-analyze使用
# 查看启动耗时
$ systemd-analyze
# 查看每个服务的启动耗时
$ systemd-analyze blame
# 显示瀑布状的启动过程流
$ systemd-analyze critical-chain
# 显示指定服务的启动流
$ systemd-analyze critical-chain atd.service
3.3 hostnamectl使用
# 显示当前主机的信息
$ hostnamectl
# 设置主机名。
$ sudo hostnamectl set-hostname rhel7
3.4 localectl使用
# 查看本地化设置
$ localectl
# 设置本地化参数。
$ sudo localectl set-locale LANG=en_GB.utf8
$ sudo localectl set-keymap en_GB
3.5 timedatectl使用
# 查看当前时区设置
$ 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
3.6 loginctl使用
# 列出当前session
$ loginctl list-sessions
# 列出当前登录用户
$ loginctl list-users
# 列出显示指定用户的信息
$ loginctl show-user ruanyf
四、systemd Unit文件
systemd 可以管理所有系统资源,不同的资源统称为 unit(单位)。 unit 文件统一了过去各种不同系统资源配置格式,例如服务的启/停、定时任务、设备自动挂载、网络配置、虚拟内存配置等。而 systemd 通过不同的文件后缀来区分这些配置文件。
4.1 systemd unit 文件类型
systemd 支持的 12 种 unit 文件类型如下:
.automount | 用于控制自动挂载文件系统,相当于 SysV-init 的 autofs 服务 |
.device | 对于 /dev 目录下的设备,主要用于定义设备之间的依赖关系 |
.mount | 定义系统结构层次中的一个挂载点,可以替代过去的 /etc/fstab 配置文件 |
.path | 用于监控指定目录或文件的变化,并触发其它 Unit 运行 |
.scope | 这种 Unit 文件不是用户创建的,而是 Systemd 运行时产生的,描述一些系统服务的分组信息 |
.service | 封装守护进程的启动、停止、重启和重载操作,是最常见的一种 Unit 文件 |
.slice | 用于表示一个 CGroup 的树,通常用户不会自己创建这样的 Unit 文件 |
.snapshot | 用于表示一个由 systemctl snapshot 命令创建的 Systemd Units 运行状态快照 |
.socket | 监控来自于系统或网络的数据消息,用于实现基于数据自动触发服务启动 |
.swap | 定义一个用户做虚拟内存的交换分区 |
.target | 用于对 Unit 文件进行逻辑分组,引导其它 Unit 的执行。它替代了 SysV-init 运行级别的作用,并提供更灵活的基于特定设备事件的启动方式 |
.timer | 用于配置在特定时间触发的任务,替代了 Crontab 的功能 |
systemctl list-units命令可以查看当前系统的所有 Unit 。
# 列出正在运行的 Unit
$ systemctl list-units
# 列出所有Unit,包括没有找到配置文件的或者启动失败的
$ systemctl list-units --all
# 列出所有没有运行的 Unit
$ systemctl list-units --all --state=inactive
# 列出所有加载失败的 Unit
$ systemctl list-units --failed
# 列出所有正在运行的、类型为 service 的 Unit
$ systemctl list-units --type=service
4.2 systemd unit 优先级
Unit 文件按照 Systemd 约定,应该被放置指定的三个系统目录之一中。这三个目录是有优先级的,如下所示,越靠上的优先级越高。因此,在三个目录中有同名文件的时候,只有优先级最高的目录里的那个文件会被使用。
/etc/systemd/system:系统或用户自定义的配置文件
/run/systemd/system:软件运行时生成的配置文件
/usr/lib/systemd/system:系统或第三方软件安装时添加的配置文件。
4.3 systemd unit 状态
# 显示系统状态
$ systemctl status
# 显示单个 Unit 的状态
$ sysystemctl status bluetooth.service
# 显示远程主机的某个 Unit 的状态
$ systemctl -H root@rhel7.example.com status httpd.service
4.4 systemd unit 管理
# 立即启动一个服务
$ sudo systemctl start apache.service
# 立即停止一个服务
$ sudo systemctl stop apache.service
# 重启一个服务
$ sudo systemctl restart apache.service
# 杀死一个服务的所有子进程
$ sudo systemctl kill apache.service
# 重新加载一个服务的配置文件
$ sudo systemctl reload apache.service
# 重载所有修改过的配置文件
$ sudo systemctl daemon-reload
# 显示某个 Unit 的所有底层参数
$ systemctl show httpd.service
# 显示某个 Unit 的指定属性的值
$ systemctl show -p CPUShares httpd.service
# 设置某个 Unit 的指定属性
$ sudo systemctl set-property httpd.service CPUShares=500
4.5 systemd unit 依赖
Unit 之间存在依赖关系:A 依赖于 B,就意味着 Systemd 在启动 A 的时候,同时会去启动 B。
#systemctl list-dependencies命令列出一个 Unit 的所有依赖
systemctl list-dependencies nginx.service
#上面命令的输出结果之中,有些依赖是 Target 类型(详见下文),默认不会展开显示
#如果要展开 Target,就需要使用--all参数
systemctl list-dependencies --all nginx.service
4.6 systemd 日志打印
# 查看所有日志(默认情况下 ,只保存本次启动的日志)
$ 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
五、systemd的启动流程
5.1 systemd 启动过程
下图是centos6的systemd启动流程:
5.2 systemd 系统服务
systemd的一些(不全)组件是:
kernel-install | 用于自动将内核及其各自的initramfs映像移动到启动分区的脚本; |
systemd-boot | 简单的 UEFI启动管理器; |
systemd-creds | 安全地存储和检索 systemd 单元使用的凭据; |
systemd-cryptenroll | 将 PKCS#11、FIDO2、TPM2 令牌/设备注册到 LUKS2 加密卷; |
systemd-firstboot | 首次启动前的基本系统设置初始化; |
systemd-homed | 便携式人类用户帐户; |
systemd-logind(8) | 会话管理; |
systemd-networkd | 网络配置管理; |
systemd-nspawn | 轻量级命名空间容器; |
systemd-resolved | 网络名称解析; |
systemd-stub(7) | 用于创建统一内核映像的UEFI 引导存根; |
systemd-sysusers(8) | 创建系统用户和组,并在软件包安装或启动时将用户添加到组中; |
systemd-timesyncd | 通过网络进行系统时间同步; |
systemd/Journal | 系统日志记录; |
systemd/Timers | 用于控制.service文件或事件的单调或实时计时器,是cron的合理替代方案 |
systemd-fsck | 文件系统检查; |
5.3 systemd-networkd
Match]
Name=eth0
[Network]
DHCP=yes
[DHCP]
RouteMetric=0
再添加一个静态 IP 配置文件 20-wired-static.network,内容如下:
[Match]
Name=eth0
[Network]
Address=192.168.1.100/24
DNS=192.168.1.1
DNS=114.114.114.114
[Route]
Gateway=192.168.1.1
Metric=100
文件名前面的数字表示优先级,数字越小优先级越高,因此上述两个配置文件会优先使用 dhcp 网络配置。
5.4 systemd-udevd
systemd-udevd的工作方式是侦听Linux内核中涉及设备状态更改的事件,当有新设备插入或拔出,或者设备状态发生变化时,内核会发出相应的事件。systemd-udevd会监听这些事件,并根据udev规则处理每个事件。
自动挂载u盘的例子如下:
automount-usb.rules:
ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", RUN{program}+="/bin/mkdir /media/%k" ,
RUN{program}+="/usr/bin/systemd-mount --no-block --collect $devnode /media/%k"
下面的表格是,rules.d的键值含义:
键 | 含义 |
ACTION | 事件 (uevent) 的行为,例如:add( 添加设备 )、remove( 删除设备 )。 |
KERNEL | 在内核里看到的设备名字,比如sd*表示任意SCSI磁盘设备 |
DEVPATH | 内核设备路径,比如/devices/* |
SUBSYSTEM | 子系统名字,例如:sda 的子系统为 block。 |
BUS | 总线的名字,比如IDE,USB |
DRIVER | 设备驱动的名字,比如ide-cdrom |
ID | 独立于内核名字的设备名字 |
SYSFS{filename} | 设备的 devpath 路径下,设备的属性文件“filename”里的内容 |
ENV{key} | 环境变量,可以表示任意 |
PROGRAM | 可执行的外部程序,如果程序返回0值,该键则认为为真(true) |
RESULT | 上一个PROGRAM调用返回的标准输出。 |
NAME | 根据这个规则创建的设备文件的文件名。 **注意:**仅仅第一行的NAME描述是有效的,后面的均忽略。如果你想使用使用两个以上的名字来访问一个设备的话,可以考虑SYMLINK键。 |
SYMLINK | 为/dev/下的设备文件产生符号链接。由于udev只能为某个设备产生一个设备文件,所以为了不覆盖系统默认的 udev 规则所产生的文件,推荐使用符号链接。 |
OWNER | 设备文件的属组 |
GROUP | 设备文件所在的组。 |
MODE | 设备文件的权限,采用8进制 |
RUN | 为设备而执行的程序列表 |
LABEL | 在配置文件里为内部控制而采用的名字标签(下下面的GOTO服务) |
GOTO | 跳到匹配的规则(通过LABEL来标识),有点类似程序语言中的GOTO |
IMPORT{type} | 导入一个文件或者一个程序执行后而生成的规则集到当前文件 |
WAIT_FOR_SYSFS | 等待一个特定的设备文件的创建。主要是用作时序和依赖问题。 |
OPTIONS | 特定的选项:last_rule 对这类设备终端规则执行; ignore_device 忽略当前规则; ignore_remove 忽略接下来的并移走请求。 all_partitions 为所有的磁盘分区创建设备文件。 |
六、在yocto中开启systemd
在local.conf文件或者DISTRO.conf文件中增加:
DISTRO_FEATURES_append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
七、systemd service文件的配置
7.1 service配置案例
下面是一个test.service的写法案例:
[Unit]
Description=Test service
Documentation=pathto/xx.doc
After=network.target
Before=xx.service
[Service]
Type=forking
ExecStart=test
WorkingDirectory=/user/bin
User=root
Restart=on-failure
[Install]
WantedBy=multi-user.target
7.2 service 配置解释
[Unit] 区块通常是配置文件的第一个区块,用来定义 Unit 的元数据,以及配置与其他 Unit 的关系。它的主要字段如下。
Description:服务描述
Documentation:文档地址
Requires:当前 Unit 依赖的其他 Unit,如果它们没有运行,当前 Unit 会启动失败
Wants:与当前 Unit 配合的其他 Unit,如果它们没有运行,当前 Unit 不会启动失败
BindsTo:与Requires类似,它指定的 Unit 如果退出,会导致当前 Unit 停止运行
Before:如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之后启动
After:如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之前启动
Conflicts:这里指定的 Unit 不能与当前 Unit 同时运行
Condition...:当前 Unit 运行必须满足的条件,否则不会运行
Assert...:当前 Unit 运行必须满足的条件,否则会报启动失败
[Service] 区块用来 Service 的配置,只有 Service 类型的 Unit 才有这个区块。它的主要字段如下。
Type:定义启动时的进程行为。它有以下几种值。
Type=simple:默认值,执行ExecStart指定的命令,启动主进程
Type=forking:以 fork 方式从父进程创建子进程,创建后父进程会立即退出
Type=oneshot:一次性进程,Systemd 会等当前服务退出,再继续往下执行
Type=dbus:当前服务通过D-Bus启动
Type=notify:当前服务启动完毕,会通知Systemd,再继续往下执行
Type=idle:若有其他任务执行完毕,当前服务才会运行
ExecStart:启动当前服务的命令
ExecStartPre:启动当前服务之前执行的命令
ExecStartPost:启动当前服务之后执行的命令
ExecReload:重启当前服务时执行的命令
ExecStop:停止当前服务时执行的命令
ExecStopPost:停止当其服务之后执行的命令
RestartSec:自动重启当前服务间隔的秒数
Restart:定义何种情况 Systemd 会自动重启当前服务,可能的值包括always(总是重启)、on-success、on-failure、on-abnormal、on-abort、on-watchdog
TimeoutSec:定义 Systemd 停止当前服务之前等待的秒数
Environment:指定环境变量
八、systemd mount文件的写法
[Unit]
Description=mount disk
[Mount]
What=/dev/mmcblk2p3
Where=/home/root/mount/
Type=ext4
Options=defaults
[Install]
WantedBy=multi-user.target
注意:.mount文件的名称需要与挂载点一致,如这里的挂载点是/home/root/mount,所以.mount文件必要名为home-root-mount.mount
九、基于yocto 开机启动服务的写法
9.1 service文件
[Unit]
Description=Test service
[Service]
Type=forking
ExecStart=systemd_test.sh
WorkingDirectory=/usr/bin
User=root
Restart=on-failure
[Install]
WantedBy=multi-user.target
9.2 yocto bb文件
SUMMARY = "systemd test"
DESCRIPTION = "systemd test"
LICENSE = "CLOSED"
SECTION = "systemd_test"
inherit systemd
SYSTEMD_AUTO_ENABLE = "enable" //是否开机启动
SYSTEMD_SERVICE_${PN} = "systemd_test.service"
SRC_URI = " \
file://systemd_test.service \
file://systemd_test.sh \
"
do_install() {
install -d ${D}${bindir}
install -d ${D}${systemd_system_unitdir}
install -m 0755 ${WORKDIR}/systemd_test.sh ${D}${bindir}/
install -m 0644 ${WORKDIR}/systemd_test.service ${D}/${systemd_unitdir}/system/systemd_test.service
}
FILES_${PN} += "${bindir}/systemd_test.sh"
FILES_${PN} += "${systemd_unitdir}/system/systemd_test.service"
参考文档:
1、https://systemd.io/
2、https://cloud.tencent.com/developer/article/1516125
3、https://ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html
4、https://systemd-book.junmajinlong.com/systemd_bootup.html
5、https://getiot.tech/zh/imx8/systemd-network-configuration
6、https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/chap-managing_services_with_systemd