systemctl命令的使用

发布时间:2021-05-15 22:28
最后更新:2021-05-15 22:28
所属分类:
DevOps 系统管理

systemd是为了替换Linux系统中init进程而提出来的,是为系统的启动和管理提供直接服务的,而从它的名字也可以看出来,作为一个守护进程,systemd就是用来守护整个系统的。在systemd提供的一整套工具中,最常用的就是systemctl命令。

systemd要解决的问题

init进程是Linux用来启动服务的传统进程。一般需要自己配置/etc/init.d的服务,都是使用init进程启动的。

使用init来启动服务不是那么容易的,首先init服务是串行的,不是并行的,不能同时运行多个进程。其次,init进程是通过脚本来启动服务,所以脚本的编写就会变得十分复杂。

systemd成功的解决了init进程的这些问题,虽然systemd引入了一个十分庞大复杂的架构体系。systemd目前在大多数的Linux发行版中都得到了应用,并且取代了init进程成为了系统的1号进程(PID: 1)。我们日常最经常用到的功能就是systemd中的系统管理功能,也就是systemctl命令。

除了systemctl命令以外,systemd还提供了许多其他的命令,用来管理系统的每个方面。例如systemd-analyze可以用来分析系统启动耗时,hostnamectl可以用来查看主机信息。

可以被管理的系统资源

作为系统的1号进程,可以管理系统中的所有资源,systemd将这些资源划分成了不同的Unit,具体可见下表。

Unit名称 对应系统资源 文件命名后缀
Service Unit 系统服务 .service
Target Unit 由多个Unit组成的一个组 .target
Device Unit 硬件设备 .device
Mount Unit 文件系统的挂载点 .mount
Automount Unit 自动挂载点 .automount
Path Unit 文件或路径 .path
Scope Unit 不由systemd启动的外部进程 .scope
Slice Unit 资源控制组 .slice
Snapshot Unit systemd快照
Socket Unit 进程间通信的套接字 .socket
Swap Unit 交换区文件 .swap
Timer Unit 任务计划 .timer

其实Unit文件在系统中就是ini格式的纯文本文件,其中书写了所有Unit对象的信息。

Unit资源加载路径

systemd会从一组在编译是就已经设定好的路径中加载Unit文件,并且不同的路径的优先级不一样,高优先级目录中的Unit文件会覆盖地优先级目录中的同名文件。鉴于systemd可以以系统实例和用户实例两种模式运行,所以会有两种Unit文件加载顺序。

以系统实例模式(--system)运行时:

系统单元目录 目录所包含内容 优先级
/etc/systemd/system.control 通过dbus API创建的永久系统单元 0
/etc/systemd/system.control 通过dbus API创建的临时系统单元 1
/etc/systemd/transient 动态配置的临时单元(系统与全局用户共用) 2
/etc/systemd/generator.early 生成的高优先级单元(系统与全局用户共用) 3
/etc/systemd/system 本地配置的系统单元 4
/run/systemd/system 运行时配置的系统单元 5
/run/systemd/generator 生成的中优先级系统单元 6
/usr/local/lib/systemd/system 本地软件包安装的系统单元 7
/usr/lib/systemd/system 发行版软件包安装的系统单元 8
/run/systemd/generator.late 生成的低优先级系统单元 9

当systemd以用户实例模式(--user)运行时,所使用的目录的基本上与以系统实例模式类似,只是system目录一般都换成user目录,系统单元也替换成用户单元,并且会受到环境变量$XDG_CONFIG_HOME$XDG_RUNTIME_HOME的影响。

一般来说,需要开机不登录就可以运行的程序,都需要存放在系统服务里,即/usr/lib/systemd/system目录里,如果需要用户登录以后才可以运行的程序,可以放在相应的用户实例模式目录中,即/usr/lib/systemd/usr目录里。

所有的优先级描述,数字越小优先级越高。

systemctl命令功能

systemctl命令就是针对这些Unit进行管理。

命令格式

1
systemctl [OPTIONS] COMMAND [UNIT]

正如systemctl命令格式中所描述的一样,systemctl命令的功能是通过不同的子命令来实现的。

列举Unit

列举Unit可以采用以下命令格式:

1
systemctl list-units [pattern]

直接运行这个命令可以列出目前已经存在于内存中的Unit,如果需要继续列出其他的Unit,那么就需要增加pattern了。常用的pattern有以下这些。

  • --all,所有的Unit,包括没有找到配置文件和启动失败的。
  • --failed,仅列出加载失败的Unit。
  • --type,仅列出指定类型的Unit。这里的type值可以使用以上表格中的Unit名称(不带Unit字样且使用小写)。
  • --state,列出拥有指定状态的Unit。可以使用的状态有:
    • active
    • reloading
    • inactive
    • failed
    • activating
    • deactivating

其他相似的列举命令还有:

  • list-sockets,列举所有内存中的套接字Unit。
  • list-timers,列举所有内存中的定时任务Unit。
  • list-unit-files,列举目前系统中已经安装的Unit文件。
  • is-active, 列举所有已经激活的Unit。
  • is-failed,列举所有加载失败的Unit。

查看Unit状态

查看Unit状态需要使用status命令,格式为:

1
systemctl status [pattern|PID]

这里的pattern则是各个Unit文件的名称了,但是不需要包括文件后缀。例如systemctl status bluetoothstatus命令将会列出指定Unit的全部信息。对于Unit的状态是通过Unit名称前面的图标来表现的。

启用和禁用Unit

支持systemd程序在安装的时候,一般都会在/usr/lib/systemd/system目录中添加一个Unit配置文件,这时就可以使用systemctl提供的命令来完成启用和禁用的任务。

systemctl中用于支持启用和禁用Unit的命令是以下两个:

  • enable,启用Unit配置。
  • disable,禁用Unit配置。

这两个命令在使用的时候均只需要书写Unit配置文件的文件名即可,不需要书写文件名后缀。这两个命令的主要是操作/etc/systemd/system目录中的符号链接。当调用enable命令的时候,systemctl会在/etc/systemd/system目录中创建一个指向/usr/lib/systemd/system中文件的符号链接。而调用disable命令的时候,systemctl会撤销/etc/systemd/system目录中的符号链接。

开机的时候,systemd只会执行/etc/systemd/system目录中的配置文件。所以如果要修改系统服务的配置,只需要修改这个目录中的文件即可。

控制Unit状态

控制Unit的状态应该是日常使用systemctl命令最频繁的地方了。常用的Unit状态控制命令主要有以下这些:

  • start,启动Unit。
  • stop,停止Unit。
  • reload,重新加载Unit的配置。
  • restart,重新启动Unit。
  • try-restart,尝试重新启动Unit,如果Unit启动失败,不会报错。
  • reload-or-restart,重新加载Unit的配置,如果Unit不支持reload,那么则以重新启动代替。
  • try-reload-or-restart,尝试重新加载Unit的配置,如果Unit既不能重新加载配置也不能重新启动,那么便什么也不做。
  • kill,强行终止Unit的运行。
  • clean,清理Unit的配置、缓存等一切运行痕迹。
  • freeze,挂起并冻结Unit的所有进程。
  • thraw,解封被冻结的Unit。

编写一个Unit配置文件

要编写一个Unit配置文件,最快的学习方法就是参考系统本身已经存在的配置文件。要查看Unit配置文件,并不需要实地去保存有配置文件的目录中使用编辑器打开,而是可以借助systemctl提供的命令:systemctl cat [Unit]

Unit配置文件就是一个ini格式的文本文件,其中可以分为三个区块:Unit、Sevice和Install。每个区块都是以下形式的键值对。

1
2
3
[Section]
Directive=value
Directive=value

Unit区块

Unit区块通常是配置文件的第一个区块,主要用来定义Unit的元信息和与其他Unit的关系。例如会形成以下这样的配置项。

1
2
3
[Unit]
Description=A Sample Service
Requires=sshd.service

以上示例表示这个Unit描述是A Sample Service,依赖于sshd.service运行。在Unit区块里可以用来定义Unit元信息的项目主要有以下这些。

  • Description,Unit的简短描述。
  • Documentation,Unit的文档所在位置。
  • Required,当前Unit所依赖于的其他Unit,只有在这些Unit启动之后,当前的Unit才会开始启动。如果列在这里被依赖的Unit没有启动,那么这些Unit会先被启动。
  • Wants,与当前Unit配合的其他Unit,如果哪些Unit不存在,当前Unit也不会出现启动失败的情况。
  • Requesite,与Required类似,但是如果列在这里的Unit没有启动,那么当前的Unit会启动失败。
  • BindsTo,当前Unit所要绑定到的其他Unit,会随目标Unit一同启动,也会随之停止。
  • PartOf,与Required类似,但是如果列在这里的Unit发生了停止或者重启事件,那么当前Unit也会跟随做相同的操作。
  • Before,指示当前Unit必须在指定Unit之前启动。
  • After,指示当前Unit必须在指定Unit之后启动。
  • Conflicts,指示当前Unit不能与指定Unit一同运行。
  • OnFailure,指示当当前Unit处于failed状态的时候,需要启动哪些Unit来进行处理。
  • FailureActionSuccessAction,指示当当前Unit处于相应的状态时,要激活哪些系统或者用户活动,可以取以下值。
    • none,不做任何动作,可在用户模式中使用。
    • reboot,重启系统。
    • reboot-force,强制重启系统。
    • reboot-immediate,强制立刻重启系统。
    • poweroff,关闭系统。
    • poweroff-force,强制隔壁系统。
    • exit,退出程序,可在用户模式中使用。
    • exit-force,强制退出,可在用户模式中使用。

除了以上这些配置项以外,还有一系列的配置项是用来定义Unit的启动条件的,这些配置项通过对一些条件的定义确定了Unit的启动时机。常用的条件配置项都是Condition...前缀格式的。,默认情况下配置项中所列举的条件都是与的关系,但是也可以使用Condition...=|...格式来将配置项变为或的关系,如果在配置值前使用了!,那么这个条件就会成为一个否定条件。

Condition...配置项可以常用的条件后缀主要有以下这些:

  • Architecture,系统架构,常用值有x86x86-64armarm-bearm64等。
  • Virtualization,判断系统是否运行在虚拟化环境中,常用值有qemukvmvmwaremicrosoftoracledockerpodmanrkt等。
  • Host,判断系统的Hostname是否是指定值。
  • KernelVersion,判断系统内核的版本,可以使用<>等关系比较符。
  • Environment,判断制定的环境变量是否已经设置。
  • Security,判断指定的安全技术已经在系统中被启用,常用的值有selinuxapparmoraudit等。
  • Capability,判断服务管理器是否拥有指定的能力。
  • ACPower,判断系统是否使用的是外接电源。
  • FirstBoot,判断系统是否是第一次启动。
  • PathExists,判断指定的路径是否存在。
  • PathIsDirectory,判断指定的路径是否是一个目录。
  • PathIsSymbolicLink,判断指定路径是否是一个符号链接。
  • PathIsMountPoint,判断指定路径是否是一个挂载点。
  • PathIsReadWrite,判断指定路径是否是可读写的。
  • PathIsEncrypted,判断指定路径是否已被加密。
  • DirectoryNotEmpty,判断指定路径是否非空。
  • FileNotEmpty,判断指定文件是否非空。
  • FileIsExecutable,判断指定文件是可执行的。
  • Memory,判断系统中所安装的内存容量是否满足要求,可以使用<>等关系比较符。
  • CPUs,判断系统中所安装的CPU数量是否满足要求,可以使用<>等关系比较符。

如果一个配置项目需要有多个值,那么可以直接用空格分隔书写。

通过对于Unit元信息的配置,可以确定Unit的启动条件,使Unit在启动的时候不至于因为缺少必要的资源而出现启动失败。

Service区块

Service区块是Service Unit专有的配置区块,其中配置的是要启动何种程序以及程序要如何启动、如何停止、如何重启等。常用的配置项有以下这些。

  • Type,程序以何种方式启动,可以取以下值。
    • simple,普通方式。
    • forking,程序以fork()方式启动。
    • oneshot,程序只执行一次,systemd会等待程序执行结束再继续启动其他服务。
    • dbus,程序会等待DBus信号以后启动。
    • notify,程序启动后会发出通知信号,systemd才会去继续启动其他服务。
    • idle,程序会等待其他任务都执行结束才会开始执行。
  • ExecStart,要启动的程序及其参数。配置值前可以添加以下符号来实现一些特殊的功能,这些符号中功能不相近的可以同时使用。
    • @,指示将第二个参数映射给argv[0]
    • -,报错信息将会被压制,只会被记录。
    • :,环境变量不会被应用进去。
    • +,程序会拥有最大权限。
    • !,程序会使用提升的权限执行。
  • ExecStartPreExecStartPost,指示在主程序启动之前或者之后启动其他的程序。
  • ExecReload,指定执行systemctl reload的时候如何激活程序的重载功能,命令中可以使用$MAINPID来引用程序的PID。
  • ExecStop,指定执行systemctl stop时使用何命令来停止程序的运行。
  • ExecStopPost,指定在程序停止之后还需要执行的命令。
  • RestartSec,指定重新启动程序的时间间隔。
  • TimeoutStartSec,指定启动程序之前需要等待的时间。
  • TimeoutStopSec,指定停止程序之前需要等待的时间。
  • Restart,程序的重启策略,可取值有noon-successon-failureon-abnormalon-watchdogon-abortalways
  • OOMPolicy,设定程序出现Out-Of-Memory状态时系统可以采取的策略,可取值有continuestopkill

Install区块

Install区块是被systemctl enablesystemctl diasble命令使用确定Unit要被如何安装和卸载的。这个区块中可以使用的配置项有以下这些。

  • Alias,定义Unit的别名。Unit的别名可以在其他的Unit定义文件中使用,但是要求一个Unit的别名必须拥有相同的后缀。并且如果一个Unit拥有多个别名,那么在执行systemctl enable命令的时候,将会创建多个符号链接。
  • WantedByRequiredBy,会在.wants.requires命名的Target目录中创建当前Unit的符号链接,使当前Unit可以被指定的Target所包含。
  • Also,设置与当前Unit一同被安装和卸载的Unit。

常见问题

设置的服务在启动的时候报Default-Start contains no runlevels

这种错误在比较旧的Linux系统中很少出现,反而在比较新的Linux系统中更容易出现。一旦出现了这种错误,服务将不会启动。但其实这种错误也十分容易解决,这种错误一般表示服务的启动脚本中没有定义Default-Start所可以使用的runlevel

要解决这个错误,只需要在服务的启动脚本开头的位置加入以下代码即可。

1
2
3
4
5
6
7
8
9
### BEGIN INIT INFO
# Provides: service-name
# Required-Start:
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description:
# Description:
### END INIT INFO

索引标签
Linux
Service
命令
systemd
systemctl