systemd 介绍
systemd是Linux下的一种init软件,由Lennart Poettering带头开发,并在LGPL2.1及其后续版本许可证下开源发布框架以表示系统服务间的依赖关系,并依此实现系统初始化时服务的并行启动,同时达到降低Shell的系统开销的效果,最终代替现在常用的System V与BSD风格init程序
systemd 基本工具
检视和控制systemd的主要命令是systemctl
。该命令可用于查看系统状态和管理系统及服务。详见man 1 systemctl
。
分析系统状态
输出激活的单元:
$ systemctl
以下命令等效:
$ systemctl list-units
输出运行失败的单元:
$ systemctl --failed
所有可用的单元文件存放在 /usr/lib/systemd/system/
和 /etc/systemd/system/
目录(后者优先级更高)。查看所有已安装服务:
$ systemctl list-unit-files
使用单元
一个单元配置文件可以描述如下内容之一:系统服务(.service
)、挂载点(.mount
)、sockets(.sockets
) 、系统设备(.device
)、交换分区(.swap
)、文件路径(.path
)、启动目标(.target
)、由 systemd 管理的计时器(.timer
)。详情参阅 man 5 systemd.unit
。
使用 systemctl
控制单元时,通常需要使用单元文件的全名,包括扩展名(例如 sshd.service
)。但是有些单元可以在systemctl
中使用简写方式。
- 如果无扩展名,systemctl 默认把扩展名当作
.service
。例如netcfg
和netcfg.service
是等价的。 - 挂载点会自动转化为相应的
.mount
单元。例如/home
等价于home.mount
。 - 设备会自动转化为相应的
.device
单元,所以/dev/sda2
等价于dev-sda2.device
。
立即激活单元:
# systemctl start <单元>
立即停止单元:
# systemctl stop <单元>
重启单元:
# systemctl restart <单元>
重新加载配置:
# systemctl reload <单元>
输出单元运行状态:
$ systemctl status <单元>
检查单元是否配置为自动启动:
$ systemctl is-enabled <单元>
开机自动激活单元:
# systemctl enable <单元>
取消开机自动激活单元:
# systemctl disable <单元>
禁用一个单元(禁用后,间接启动也是不可能的):
# systemctl mask <单元>
取消禁用一个单元:
# systemctl unmask <单元>
显示单元的手册页(必须由单元文件提供):
# systemctl help <单元>
重新载入 systemd,扫描新的或有变动的单元:
# systemctl daemon-reload
电源管理
安装 polkit 后才可以一般用户身份使用电源管理。
如果你正登录在一个本地的systemd-logind
用户会话,且当前没有其它活动的会话,那么以下命令无需root权限即可执行。否则(例如,当前有另一个用户登录在某个tty),systemd 将会自动请求输入root密码。
重启:
$ systemctl reboot
退出系统并停止电源:
$ systemctl poweroff
待机:
$ systemctl suspend
休眠:
$ systemctl hibernate
混合休眠模式(同时休眠到硬盘并待机):
$ systemctl hybrid-sleep
编写单元文件
systemd
单元文件的语法来源于 XDG桌面入口配置文件.desktop
文件,最初的源头则是Microsoft Windows的.ini
文件。单元文件可以从两个地方加载,优先级从低到高分别是:
/usr/lib/systemd/system/
: 软件包安装的单元/etc/systemd/system/
: 系统管理员安装的单元
单元文件的语法,可以参考系统已经安装的单元,也可以参考man systemd.service
中的EXAMPLES章节。
处理依赖关系
使用systemd时,可通过正确编写单元配置文件来解决其依赖关系。典型的情况是,单元A
要求单元B
在A
启动之前运行。在此情况下,向单元A
配置文件中的 [Unit]
段添加 Requires=B
和 After=B
即可。若此依赖关系是可选的,可添加 Wants=B
和 After=B
。请注意 Wants=
和 Requires=
并不意味着 After=
,即如果 After=
选项没有制定,这两个单元将被并行启动。
依赖关系通常被用在服务(service)而不是目标(target)上。例如, network.target
一般会被某个配置网络接口的服务引入,所以,将自定义的单元排在该服务之后即可,因为 network.target
已经启动。
服务类型
编写自定义的 service 文件时,可以选择几种不同的服务启动方式。启动方式可通过配置文件 [Service]
段中的 Type=
参数进行设置。
Type=simple
(默认值):systemd认为该服务将立即启动。服务进程不会fork。如果该服务要启动其他服务,不要使用此类型启动,除非该服务是socket激活型。Type=forking
:systemd认为当该服务进程fork,且父进程退出后服务启动成功。对于常规的守护进程(daemon),除非你确定此启动方式无法满足需求,使用此类型启动即可。使用此启动类型应同时指定PIDFile=
,以便systemd能够跟踪服务的主进程。Type=oneshot
:这一选项适用于只执行一项任务、随后立即退出的服务。可能需要同时设置RemainAfterExit=yes
使得 systemd 在服务进程退出之后仍然认为服务处于激活状态。Type=notify
:与Type=simple
相同,但约定服务会在就绪后向 systemd 发送一个信号。这一通知的实现由libsystemd-daemon.so
提供。Type=dbus
:若以此方式启动,当指定的BusName
出现在DBus系统总线上时,systemd认为服务就绪。Type=idle
:systemd
会等待所有任务(Jobs)处理完成后,才开始执行idle
类型的单元。除此之外,其他行为和Type=simple
类似。
type
的更多解释可以参考 systemd.service(5)。
修改现存单元文件
要更改由软件包提供的单元文件,先创建名为 /etc/systemd/system/<单元名>.d/
的目录(如 /etc/systemd/system/httpd.service.d/
),然后放入 *.conf
文件,其中可以添加或重置参数。这里设置的参数优先级高于原来的单元文件。例如,如果想添加一个额外的依赖,创建这么一个文件即可:
/etc/systemd/system/<unit>.d/customdependency.conf
[Unit] Requires=<新依赖> After=<新依赖>
As another example, in order to replace the ExecStart
directive for a unit that is not of type oneshot
, create the following file:
/etc/systemd/system/unit.d/customexec.conf
[Service] ExecStart= ExecStart=new command
想知道为什么修改 ExecStart
前必须将其置空,参见 ([1]).
下面是自动重启服务的一个例子:
/etc/systemd/system/unit.d/restart.conf
[Service] Restart=always RestartSec=30
然后运行以下命令使更改生效:
# systemctl daemon-reload # systemctl restart <单元>
此外,把旧的单元文件从 /usr/lib/systemd/system/
复制到 /etc/systemd/system/
,然后进行修改,也可以达到同样效果。在 /etc/systemd/system/
目录中的单元文件的优先级总是高于 /usr/lib/systemd/system/
目录中的同名单元文件。注意,当 /usr/lib/
中的单元文件因软件包升级变更时,/etc/
中自定义的单元文件不会同步更新。此外,你还得执行 systemctl reenable <unit>
,手动重新启用该单元。因此,建议使用前面一种利用 *.conf
的方法。
可从 官方仓库 安装 vim-systemd 软件包,可以使 unit 配置文件在 Vim 下支持语法高亮。
目标(target)
启动级别(runlevel)是一个旧的概念。现在,systemd 引入了一个和启动级别功能相似又不同的概念——目标(target)。不像数字表示的启动级别,每个目标都有名字和独特的功能,并且能同时启用多个。一些目标继承其他目标的服务,并启动新服务。systemd 提供了一些模仿 sysvinit 启动级别的目标,仍可以使用旧的 telinit 启动级别
命令切换。
获取当前目标
不要使用 runlevel
命令了:
$ systemctl list-units --type=target
创建新目标
在 Fedora 中,启动级别 0、1、3、5、6 都被赋予特定用途,并且都对应一个 systemd 的目标。然而,没有什么很好的移植用户定义的启动级别(2、4)的方法。要实现类似功能,可以以原有的启动级别为基础,创建一个新的目标 /etc/systemd/system/<新目标>
(可以参考 /usr/lib/systemd/system/graphical.target
),创建 /etc/systemd/system/<新目标>.wants
目录,向其中加入额外服务的链接(指向 /usr/lib/systemd/system/
中的单元文件)。
目标表
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) |
切换启动级别/目标
systemd 中,启动级别通过“目标单元”访问。通过如下命令切换:
# systemctl isolate graphical.target
该命令对下次启动无影响。等价于telinit 3
或 telinit 5
。
修改默认启动级别/目标
开机启动进的目标是 default.target
,默认链接到 graphical.target
(大致相当于原来的启动级别5)。可以通过内核参数更改默认启动级别:
systemd.unit=multi-user.target
(大致相当于级别3)systemd.unit=rescue.target
(大致相当于级别1)
另一个方法是修改 default.target
。可以通过 systemctl
修改它:
# systemctl set-default multi-user.target
要覆盖已经设置的default.target,请使用 force:
# systemctl set-default -f multi-user.target
可以在 systemctl
的输出中看到命令执行的效果:链接 /etc/systemd/system/default.target
被创建,指向新的默认启动级别。
临时文件
/usr/lib/tmpfiles.d/
和 /etc/tmpfiles.d/
中的文件描述了 systemd-tmpfiles 如何创建、清理、删除临时文件和目录,这些文件和目录通常存放在 /run
和 /tmp
中。配置文件名称为 /etc/tmpfiles.d/<program>.conf
。此处的配置能覆盖 /usr/lib/tmpfiles.d/
目录中的同名配置。
临时文件通常和服务文件同时提供,以生成守护进程需要的文件和目录。例如 Samba 服务需要目录 /run/samba
存在并设置正确的权限位,就象这样:
/usr/lib/tmpfiles.d/samba.conf
D /run/samba 0755 root root
此外,临时文件还可以用来在开机时向特定文件写入某些内容。比如,要禁止系统从USB设备唤醒,利用旧的 /etc/rc.local
可以用 echo USBE > /proc/acpi/wakeup
,而现在可以这么做:
/etc/tmpfiles.d/disable-usb-wake.conf
w /proc/acpi/wakeup - - - - USBE
详情参见systemd-tmpfiles(8)
和 man 5 tmpfiles.d
。
定时器
定时器是以 .timer 为后缀的配置文件,记录由system的里面由时间触发的动作, 定时器可以替代 cron 的大部分功能。详情参阅 systemd/Timers (简体中文).
日志
systemd 提供了自己日志系统(logging system),称为 journal. 使用 systemd 日志,无需额外安装日志服务(syslog)。读取日志的命令:
# journalctl
默认情况下(当 Storage=
在文件 /etc/systemd/journald.conf
中被设置为 auto
),日志记录将被写入 /var/log/journal/
。该目录是 systemd 软件包的一部分。若被删除,systemd 不会自动创建它,直到下次升级软件包时重建该目录。如果该目录缺失,systemd 会将日志记录写入 /run/systemd/journal
。这意味着,系统重启后日志将丢失。
过滤输出
journalctl
可以根据特定字段过滤输出。如果过滤的字段比较多,需要较长时间才能显示出来。
示例:
显示本次启动后的所有日志:
# journalctl -b
不过,一般大家更关心的不是本次启动后的日志,而是上次启动时的(例如,刚刚系统崩溃了)。可以使用 -b
参数:
journalctl -b -0
显示本次启动的信息journalctl -b -1
显示上次启动的信息journalctl -b -2
显示上上次启动的信息journalctl -b -2
- Show all messages from date (and optional time):
# journalctl --since="2012-10-30 18:17:16"
- Show all messages since 20 minutes ago:
# journalctl --since "20 min ago"
- 显示最新信息
# journalctl -f
- 显示特定程序的所有消息:
# journalctl /usr/lib/systemd/systemd
- 显示特定进程的所有消息:
# journalctl _PID=1
- 显示指定单元的所有消息:
# journalctl -u netcfg
- Show kernel ring buffer:
# journalctl -k
- Show auth.log equivalent by filtering on syslog facility:
# journalctl -f -l SYSLOG_FACILITY=10
详情参阅man journalctl
、man systemd.journal-fields
,以及 Lennert 的这篇博文。
日志大小限制
如果按上面的操作保留日志的话,默认日志最大限制为所在文件系统容量的 10%,即:如果 /var/log/journal
储存在 50GiB 的根分区中,那么日志最多存储 5GiB 数据。可以修改 /etc/systemd/journald.conf
中的 SystemMaxUse
来指定该最大限制。如限制日志最大 50MiB:
SystemMaxUse=50M
详情参见 man journald.conf
.
配合 syslog 使用
systemd 提供了 socket /run/systemd/journal/syslog
,以兼容传统日志服务。所有系统信息都会被传入。要使传统日志服务工作,需要让服务链接该 socket,而非 /dev/log
(官方说明)。Arch 软件仓库中的syslog-ng 已经包含了需要的配置。
As of systemd 216 the default journald.conf
for forwarding to the socket is no
. This means you will need to set the option ForwardToSyslog=yes
in /etc/systemd/journald.conf
to actually use syslog-ng with journald. See Syslog-ng#Overview for details.
If you use rsyslog instead, it is not necessary to change the option because rsyslog pulls the messages from the journal by itself.
设置开机启动 syslog-ng:
# systemctl enable syslog-ng
这里有一份很不错的 journalctl
指南。
Forward journald to /dev/tty12
In /etc/systemd/journald.conf
enable the following:
ForwardToConsole=yes TTYPath=/dev/tty12 MaxLevelConsole=info
Restart journald with:
# systemctl restart systemd-journald
疑难解答
Investigating systemd errors
As an example, we will investigate an error with systemd-modules-load
service:
1. Lets find the systemd services which fail to start:
$ systemctl --state=failed
systemd-modules-load.service loaded failed failed Load Kernel Modules
2. Ok, we found a problem with systemd-modules-load
service. We want to know more:
$ systemctl status systemd-modules-load
systemd-modules-load.service - Load Kernel Modules Loaded: loaded (/usr/lib/systemd/system/systemd-modules-load.service; static) Active: failed (Result: exit-code) since So 2013-08-25 11:48:13 CEST; 32s ago Docs: man:systemd-modules-load.service(8). man:modules-load.d(5) Process: 15630 ExecStart=/usr/lib/systemd/systemd-modules-load (code=exited, status=1/FAILURE)
If the Process ID
is not listed, just restart the failed service with systemctl restart systemd-modules-load
3. Now we have the process id (PID) to investigate this error in depth. Enter the following command with the current Process ID
(here: 15630):
$ journalctl -b _PID=15630
-- Logs begin at Sa 2013-05-25 10:31:12 CEST, end at So 2013-08-25 11:51:17 CEST. -- Aug 25 11:48:13 mypc systemd-modules-load[15630]: Failed to find module 'blacklist usblp' Aug 25 11:48:13 mypc systemd-modules-load[15630]: Failed to find module 'install usblp /bin/false'
4. We see that some of the kernel module configs have wrong settings. Therefore we have a look at these settings in /etc/modules-load.d/
:
$ ls -Al /etc/modules-load.d/
... -rw-r--r-- 1 root root 79 1. Dez 2012 blacklist.conf -rw-r--r-- 1 root root 1 2. Mär 14:30 encrypt.conf -rw-r--r-- 1 root root 3 5. Dez 2012 printing.conf -rw-r--r-- 1 root root 6 14. Jul 11:01 realtek.conf -rw-r--r-- 1 root root 65 2. Jun 23:01 virtualbox.conf ...
5. The Failed to find module 'blacklist usblp'
error message might be related to a wrong setting inside of blacklist.conf
. Lets deactivate it with inserting a trailing # before each option we found via step 3:
/etc/modules-load.d/blacklist.conf
# blacklist usblp # install usblp /bin/false
6. Now, try to start systemd-modules-load
:
$ systemctl start systemd-modules-load
If it was successful, this should not prompt anything. If you see any error, go back to step 3 and use the new PID for solving the errors left.
If everything is ok, you can verify that the service was started successfully with:
$ systemctl status systemd-modules-load
systemd-modules-load.service - Load Kernel Modules Loaded: loaded (/usr/lib/systemd/system/systemd-modules-load.service; static) Active: active (exited) since So 2013-08-25 12:22:31 CEST; 34s ago Docs: man:systemd-modules-load.service(8) man:modules-load.d(5) Process: 19005 ExecStart=/usr/lib/systemd/systemd-modules-load (code=exited, status=0/SUCCESS) Aug 25 12:22:31 mypc systemd[1]: Started Load Kernel Modules.
Often you can solve these kind of problems like shown above. For further investigation look at #Diagnosing boot problems.
诊断启动问题
使用如下内核参数引导: systemd.log_level=debug systemd.log_target=kmsg log_buf_len=1M
更多有关调试的信息,参见该文。
Diagnosing problems with a specific service
If some systemd service misbehaves and you want to get more information about what is going on, set the SYSTEMD_LOG_LEVEL
environment variable to debug
. For example, to run the systemd-networkd daemon in debug mode:
# systemctl stop systemd-networkd # SYSTEMD_LOG_LEVEL=debug /lib/systemd/systemd-networkd
Or, equivalently, modify the service file temporarily for gathering enough output. For example:
/lib/systemd/system/systemd-networkd.service
[Service] ... Environment=SYSTEMD_LOG_LEVEL=debug ....
If debug information is required long-term, add the variable the regular way.
关机/重启十分缓慢
如果关机特别慢(甚至跟死机了一样),很可能是某个拒不退出的服务在作怪。systemd 会等待一段时间,然后再尝试杀死它。请阅读这篇文章,确认你是否是该问题受害者。
短时进程无日志记录
若 journalctl -u foounit.service
没有显示某个短时进程的任何输出,那么改用 PID 试试。例如,若 systemd-modules-load.service
执行失败,那么先用 systemctl status systemd-modules-load
查询其 PID(比如是123),然后检索该 PID 相关的日志 journalctl -b _PID=123
。运行时进程的日志元数据(诸如 _SYSTEMD_UNIT 和 _COMM)被乱序收集在 /proc
目录。要修复该问题,必须修改内核,使其通过套接字连接来提供上述数据,该过程类似于 SCM_CREDENTIALS。
禁止在程序崩溃时转储内存
要使用老的内核转储,创建下面文件:
/etc/sysctl.d/49-coredump.conf
kernel.core_pattern = core kernel.core_uses_pid = 0
然后运行:
# /usr/lib/systemd/systemd-sysctl
同样可能需要执行“unlimit”设置文件大小:
$ ulimit -c unlimited
更多信息请参阅 sysctl.d 和 /proc/sys/kernel 文档。
Error message on reboot or shutdown
cgroup : option or name mismatch, new: 0x0 "", old: 0x4 "systemd"
See this thread for an explanation.
watchdog watchdog0: watchdog did not stop!
See this thread for an explanation.
Boot time increasing over time
After using systemd-analyze
a number of users have noticed that their boot time has increased significantly in comparison with what it used to be. After using systemd-analyze blame
NetworkManager is being reported as taking an unusually large amount of time to start.
The problem for some users has been due to /var/log/journal
becoming too large. This may have other impacts on performance, such as for systemctl status
or journalctl
. As such the solution is to remove every file within the folder (ideally making a backup of it somewhere, at least temporarily) and then setting a journal file size limit as described in #Journal size limit.
相关资源
更多推荐
所有评论(0)