目前我所掌握的使一个Linux下的软件开机自启动的方法只有将其加入rc.local文件或桌面环境的autostart文件中,但前者不能很好的保证软件的依赖关系,后者要求系统具备桌面环境。虽然知道现在大多数发行版已经使用systemd来管理系统服务,但一直没有研究过如何编写其单元文件,为了解决上述问题,研究了一下systemdservice单元文件的写法,将遇到的问题做个总结。

systemctl

systemctl命令基本相当于System V initservice命令,可用于系统服务的管理,也可以进行电源管理,常用命令如下:

  • 系统服务管理
1
2
3
4
5
6
7
8
9
10
11
12
systemctl enable <unit>     # 将<unit>设置为开机自启动
systemctl disable <unit> # 取消<unit>开机自启动

systemctl start <unit> # 启动<unit>
systemctl restart <unit> # 重启<unit>
systemctl stop <unit> # 停止<unit>
systemctl status <unit> # 查看<unit>的状态
systemctl reload <unit> # 重新加载<unit>的配置文件而不关闭服务

systemctl is-active <unit> # 查看<unit>是否为激活状态
systemctl is-enabled <unit> # 查看<unit>是否设置了开机自启
systemctl is-failed <unit> # 查看<unit>是否加载失败
  • 电源管理
1
2
3
4
systemctl reboot            # 重启
systemctl poweroff # 关机
systemclt hibernate # 休眠
systemctl suspend # 待机

systemd单元文件

systemd的单元文件有以下几类:

  • 系统服务 .service
  • 挂载点 .mount
  • sockets .socket
  • 系统设备 .device
  • 交换分区 .swp
  • 文件路径 .path
  • 启动目标 .target
  • 定时器 .timer

当使用systemctl对一个单元进行操作时,一般需要使用单元的全名,如ssh.service,但如果使用不带后缀名的单元名称,systemctl将会把这个单元当做系统服务(.service)进行操作。如果一个单元文件中存在@字符,表示该单元文件是一个模板单元,当使用systemctl操作模板单元时,需要对单元进行实例化,否则会调用失败。

单元文件可以从两个地方加载:

  • /usr/lib/systemd/system/:软件包安装的单元
  • /etc/systemd/system/:系统管理员安装的单元

加载优先级上,系统管理员安装的单元优先级高于软件包安装的单元。

Serivce单元文件编写

service单元文件有三个段落:[Unit] [Service] [Install];其单元文件的编写模板可以参考man手册的EXAMPLE章节,上面提到的单元加载目录下也有很多例子可供参考。

依赖关系的处理

如果需要A单元在B单元启动之后启动,仅指定Requires=BWants=B是不行的,如果不指定After=BA单元和B单元会并行启动。为了保证AB单元启动之后再启动,应该在A的配置文件中[Unit]段中添加Requires=BAfter=B

[Unit]段中,用于表示依赖关系的选项有WantsRequiresBindsToPartOf,他们所表示的依赖关系的强弱可以大致表示为

1
PartOf = BindsTo > Requires > Wants

Wants的依赖关系最弱,当依赖的单元启动失败时,不会对其他单元的启动造成影响;Requires所指定的单元中有一个启动失败时,其他相关的单元都不会被启动;BindsTo的依赖性比Requires更强,当启动使用了BindsTo的单元时,BindsTo所指定的单元均会被启动,当列出的单元全部被启动后,该单元也会被启动,但是如果指定的单元中任意一个终止或重启,该单元也会终止或重启;PartOf类似于BindsTo,不同的是,使用PartOf的单元不会随着依赖单元的启动而启动。

为启动的服务设置环境变量

可以在单元配置文件的[Service]段落中添加Environment选项,例如

1
Environment=LANG=zh_CN.UTF_8

如果需要添加多个环境变量,应在[Service]中添加多个Environment,而不是在一个Environment后面添加多个环境变量的值。 可以在[Service]中添加多个Environment,也可以在Environment后添加多个环境变量的定义,使用空格分隔,如:

1
Environment=PATH=/home/jack CONFIG='-std=c99'

对没错也可以使用',可以查看man手册的相关章节获得更多信息。

也可以使用EnvironmentFile选项指定一个包含环境变量列表的文件路径,这个文件中每一行都是一个环境变量的值。例如,单元文件中[Service]字段包含选项如下:

1
EnvironmentFile=/home/jack/env

/home/jack/env文件包含内容格式如下:

1
2
LANG=zh_CN.UTF-8
CONFIGPATH=/home/jack/.config/config

设置运行服务的用户和组

在单元配置文件的[Service]段落中添加UserGroup选项即可,如

1
2
User=Jack
Group=Jack

设置服务的工作路径

在单元配置文件的[Service]段落中添加WorkingDirectory选项,如

1
WorkingDirectory=/home/Jack/

总结

经过了激烈的讨论之后绝大多数发行版还是迁移到了Systemd,这个东西确实符合UNIXkeep it simple and stupid的哲学,而是一个大而全的东西。Systemd出现后接管了Linux上包括启动日志在内的很多东西,造成使用者们之前掌握的一部分技能在这上面不顶用了,这会不会也是Systemd出现后遭到抵制的原因之一呢。

更多Systemd的相关资料可以查看man手册的systemd章节,以及ArchLinux Wiki页面中关于Systemd的部分