上一篇文章,我介绍了 Systemd 的主要命令,今天介绍如何使用它完成一些基本的任务。
一、开机启动
对于那些支持 Systemd 的软件,安装的时候,会自动在/usr/lib/systemd/system
目录添加一个配置文件。
如果你想让该软件开机启动,就执行下面的命令(以httpd.service
为例)。
$ sudo systemctl enable httpd
上面的命令相当于在/etc/systemd/system
目录添加一个符号链接,指向/usr/lib/systemd/system
里面的httpd.service
文件。
这是因为开机时,Systemd
只执行/etc/systemd/system
目录里面的配置文件。这也意味着,如果把修改后的配置文件放在该目录,就可以达到覆盖原始配置的效果。
二、启动服务
设置开机启动以后,软件并不会立即启动,必须等到下一次开机。如果想现在就运行该软件,那么要执行systemctl start
命令。
$ sudo systemctl start httpd
执行上面的命令以后,有可能启动失败,因此要用systemctl status
命令查看一下该服务的状态。
$ sudo systemctl status httpd httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled) Active: active (running) since 金 2014-12-05 12:18:22 JST; 7min ago Main PID: 4349 (httpd) Status: "Total requests: 1; Current requests/sec: 0; Current traffic: 0 B/sec" CGroup: /system.slice/httpd.service ├─4349 /usr/sbin/httpd -DFOREGROUND ├─4350 /usr/sbin/httpd -DFOREGROUND ├─4351 /usr/sbin/httpd -DFOREGROUND ├─4352 /usr/sbin/httpd -DFOREGROUND ├─4353 /usr/sbin/httpd -DFOREGROUND └─4354 /usr/sbin/httpd -DFOREGROUND 12月 05 12:18:22 localhost.localdomain systemd[1]: Starting The Apache HTTP Server... 12月 05 12:18:22 localhost.localdomain systemd[1]: Started The Apache HTTP Server. 12月 05 12:22:40 localhost.localdomain systemd[1]: Started The Apache HTTP Server.
上面的输出结果含义如下。
Loaded
行:配置文件的位置,是否设为开机启动Active
行:表示正在运行Main PID
行:主进程IDStatus
行:由应用本身(这里是 httpd )提供的软件当前状态CGroup
块:应用的所有子进程- 日志块:应用的日志
三、停止服务
终止正在运行的服务,需要执行systemctl stop
命令。
$ sudo systemctl stop httpd.service
有时候,该命令可能没有响应,服务停不下来。这时候就不得不"杀进程"了,向正在运行的进程发出kill
信号。
$ sudo systemctl kill httpd.service
此外,重启服务要执行systemctl restart
命令。
$ sudo systemctl restart httpd.service
四、读懂配置文件
一个服务怎么启动,完全由它的配置文件决定。下面就来看,配置文件有些什么内容。
前面说过,配置文件主要放在/usr/lib/systemd/system
目录,也可能在/etc/systemd/system
目录。找到配置文件以后,使用文本编辑器打开即可。
systemctl cat
命令可以用来查看配置文件,下面以sshd.service
文件为例,它的作用是启动一个 SSH 服务器,供其他用户以 SSH 方式登录。
$ systemctl cat sshd.service [Unit] Description=OpenSSH server daemon Documentation=man:sshd(8) man:sshd_config(5) After=network.target sshd-keygen.service Wants=sshd-keygen.service [Service] EnvironmentFile=/etc/sysconfig/sshd ExecStart=/usr/sbin/sshd -D $OPTIONS ExecReload=/bin/kill -HUP $MAINPID Type=simple KillMode=process Restart=on-failure RestartSec=42s [Install] WantedBy=multi-user.target
可以看到,配置文件分成几个区块,每个区块包含若干条键值对。
下面依次解释每个区块的内容。
五、 [Unit] 区块:启动顺序与依赖关系。
Unit
区块的Description
字段给出当前服务的简单描述,Documentation
字段给出文档位置。
接下来的设置是启动顺序和依赖关系,这个比较重要。
After
字段:表示如果network.target
或sshd-keygen.service
需要启动,那么sshd.service
应该在它们之后启动。
相应地,还有一个Before
字段,定义sshd.service
应该在哪些服务之前启动。
注意,After
和Before
字段只涉及启动顺序,不涉及依赖关系。
举例来说,某 Web 应用需要 postgresql 数据库储存数据。在配置文件中,它只定义要在 postgresql 之后启动,而没有定义依赖 postgresql 。上线后,由于某种原因,postgresql 需要重新启动,在停止服务期间,该 Web 应用就会无法建立数据库连接。
设置依赖关系,需要使用Wants
字段和Requires
字段。
Wants
字段:表示sshd.service
与sshd-keygen.service
之间存在"弱依赖"关系,即如果"sshd-keygen.service"启动失败或停止运行,不影响sshd.service
继续执行。
Requires
字段则表示"强依赖"关系,即如果该服务启动失败或异常退出,那么sshd.service
也必须退出。
注意,Wants
字段与Requires
字段只涉及依赖关系,与启动顺序无关,默认情况下是同时启动的。
六、[Service] 区块:启动行为
Service
区块定义如何启动当前服务。
6.1 启动命令
许多软件都有自己的环境参数文件,该文件可以用EnvironmentFile
字段读取。
EnvironmentFile
字段:指定当前服务的环境参数文件。该文件内部的key=value
键值对,可以用$key
的形式,在当前配置文件中获取。
上面的例子中,sshd 的环境参数文件是/etc/sysconfig/sshd
。
配置文件里面最重要的字段是ExecStart
。
ExecStart
字段:定义启动进程时执行的命令。
上面的例子中,启动sshd
,执行的命令是/usr/sbin/sshd -D $OPTIONS
,其中的变量$OPTIONS
就来自EnvironmentFile
字段指定的环境参数文件。
与之作用相似的,还有如下这些字段。
ExecReload
字段:重启服务时执行的命令ExecStop
字段:停止服务时执行的命令ExecStartPre
字段:启动服务之前执行的命令ExecStartPost
字段:启动服务之后执行的命令ExecStopPost
字段:停止服务之后执行的命令
请看下面的例子。
[Service] ExecStart=/bin/echo execstart1 ExecStart= ExecStart=/bin/echo execstart2 ExecStartPost=/bin/echo post1 ExecStartPost=/bin/echo post2
上面这个配置文件,第二行ExecStart
设为空值,等于取消了第一行的设置,运行结果如下。
execstart2 post1 post2
所有的启动设置之前,都可以加上一个连词号(-
),表示"抑制错误",即发生错误的时候,不影响其他命令的执行。比如,EnvironmentFile=-/etc/sysconfig/sshd
(注意等号后面的那个连词号),就表示即使/etc/sysconfig/sshd
文件不存在,也不会抛出错误。
6.2 启动类型
Type
字段定义启动类型。它可以设置的值如下。
- simple(默认值):
ExecStart
字段启动的进程为主进程- forking:
ExecStart
字段将以fork()
方式启动,此时父进程将会退出,子进程将成为主进程- oneshot:类似于
simple
,但只执行一次,Systemd 会等它执行完,才启动其他服务- dbus:类似于
simple
,但会等待 D-Bus 信号后启动- notify:类似于
simple
,启动结束后会发出通知信号,然后 Systemd 再启动其他服务- idle:类似于
simple
,但是要等到其他任务都执行完,才会启动该服务。一种使用场合是为让该服务的输出,不与其他服务的输出相混合
下面是一个oneshot
的例子,笔记本电脑启动时,要把触摸板关掉,配置文件可以这样写。
[Unit] Description=Switch-off Touchpad [Service] Type=oneshot ExecStart=/usr/bin/touchpad-off [Install] WantedBy=multi-user.target
上面的配置文件,启动类型设为oneshot
,就表明这个服务只要运行一次就够了,不需要长期运行。
如果关闭以后,将来某个时候还想打开,配置文件修改如下。
[Unit] Description=Switch-off Touchpad [Service] Type=oneshot ExecStart=/usr/bin/touchpad-off start ExecStop=/usr/bin/touchpad-off stop RemainAfterExit=yes [Install] WantedBy=multi-user.target
上面配置文件中,RemainAfterExit
字段设为yes
,表示进程退出以后,服务仍然保持执行。这样的话,一旦使用systemctl stop
命令停止服务,ExecStop
指定的命令就会执行,从而重新开启触摸板。
6.3 重启行为
Service
区块有一些字段,定义了重启行为。
KillMode
字段:定义 Systemd 如何停止 sshd 服务。
上面这个例子中,将KillMode
设为process
,表示只停止主进程,不停止任何sshd 子进程,即子进程打开的 SSH session 仍然保持连接。这个设置不太常见,但对 sshd 很重要,否则你停止服务的时候,会连自己打开的 SSH session 一起杀掉。
KillMode
字段可以设置的值如下。
- control-group(默认值):当前控制组里面的所有子进程,都会被杀掉
- process:只杀主进程
- mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
- none:没有进程会被杀掉,只是执行服务的 stop 命令。
接下来是Restart
字段。
Restart
字段:定义了 sshd 退出后,Systemd 的重启方式。
上面的例子中,Restart
设为on-failure
,表示任何意外的失败,就将重启sshd。如果 sshd 正常停止(比如执行systemctl stop
命令),它就不会重启。
Restart
字段可以设置的值如下。
- no(默认值):退出后不会重启
- on-success:只有正常退出时(退出状态码为0),才会重启
- on-failure:非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启
- on-abnormal:只有被信号终止和超时,才会重启
- on-abort:只有在收到没有捕捉到的信号终止时,才会重启
- on-watchdog:超时退出,才会重启
- always:不管是什么退出原因,总是重启
对于守护进程,推荐设为on-failure
。对于那些允许发生错误退出的服务,可以设为on-abnormal
。
最后是RestartSec
字段。
RestartSec
字段:表示 Systemd 重启服务之前,需要等待的秒数。上面的例子设为等待42秒。
七、[Install] 区块
Install
区块,定义如何安装这个配置文件,即怎样做到开机启动。
WantedBy
字段:表示该服务所在的 Target。
Target
的含义是服务组,表示一组服务。WantedBy=multi-user.target
指的是,sshd 所在的 Target 是multi-user.target
。
这个设置非常重要,因为执行systemctl enable sshd.service
命令时,sshd.service
的一个符号链接,就会放在/etc/systemd/system
目录下面的multi-user.target.wants
子目录之中。
Systemd 有默认的启动 Target。
$ systemctl get-default multi-user.target
上面的结果表示,默认的启动 Target 是multi-user.target
。在这个组里的所有服务,都将开机启动。这就是为什么systemctl enable
命令能设置开机启动的原因。
使用 Target 的时候,systemctl list-dependencies
命令和systemctl isolate
命令也很有用。
# 查看 multi-user.target 包含的所有服务 $ systemctl list-dependencies multi-user.target # 切换到另一个 target # shutdown.target 就是关机状态 $ sudo systemctl isolate shutdown.target
一般来说,常用的 Target 有两个:一个是multi-user.target
,表示多用户命令行状态;另一个是graphical.target
,表示图形用户状态,它依赖于multi-user.target
。官方文档有一张非常清晰的 Target 依赖关系图。
八、Target 的配置文件
Target 也有自己的配置文件。
$ systemctl cat multi-user.target [Unit] Description=Multi-User System Documentation=man:systemd.special(7) Requires=basic.target Conflicts=rescue.service rescue.target After=basic.target rescue.service rescue.target AllowIsolate=yes
注意,Target 配置文件里面没有启动命令。
上面输出结果中,主要字段含义如下。
Requires
字段:要求basic.target
一起运行。
Conflicts
字段:冲突字段。如果rescue.service
或rescue.target
正在运行,multi-user.target
就不能运行,反之亦然。
After
:表示multi-user.target
在basic.target
、rescue.service
、rescue.target
之后启动,如果它们有启动的话。
AllowIsolate
:允许使用systemctl isolate
命令切换到multi-user.target
。
九、修改配置文件后重启
修改配置文件以后,需要重新加载配置文件,然后重新启动相关服务。
# 重新加载配置文件 $ sudo systemctl daemon-reload # 重启相关服务 $ sudo systemctl restart foobar
(完)
taoyu 说:
很清晰实用。赞
2016年3月 9日 12:04 | # | 引用
yak 说:
是不是 typo 了?
2016年3月 9日 17:54 | # | 引用
阮一峰 说:
确实写错了,我改一下。
2016年3月 9日 18:35 | # | 引用
Dio 说:
請問修改了systemd自己的配置檔後,例如/etc/systemd/system.conf
,是否一定需要重啟機器才會生效?
我試了一下daemon-reload與restart單一的unit,配置還是不會生效
2016年3月11日 16:40 | # | 引用
阮一峰 说:
@dio:
查了systemd.conf(5),里面也没说,看来是需要重启机器了。
2016年3月11日 21:48 | # | 引用
TeachCourse 说:
玩技术的,有时候写出来的东西,感觉懂的人自然容易看明白,不懂的感觉很吃力。。
2016年3月11日 23:44 | # | 引用
Daniel Lv 说:
我想搞明白的是 systemd 如何兼容 upstart 以及 system V 的?
如果安装了 Ubuntu 15.10 Server (当下的最新版本),会发现 “/etc/systemd” 和 "/etc/init" 以及 "/etc/init.d" 下都有各自的服务进程配置文件,互相之间的关系又是什么呢?
2016年3月12日 11:00 | # | 引用
rambone 说:
ll /sbin/init 就清楚了 应该是systemd的link文件
2016年3月14日 19:45 | # | 引用
Yiutto 说:
最近在学习centos,安装的是centos7.2,
后发现每次关机时,电源不断电,每次都要我强制关机才能切断电源。
网上一些powoff -p now, halt、init 0 等命令,都没有用
请问大神有什么方法,能每次点power off就会关机,断电源
2016年10月27日 13:57 | # | 引用
Alxsc 说:
试试 sudo systemctl poweroff 命令, 在大神的上一篇 “Systemd 入门教程:命令篇” 中有提及。
2016年10月28日 16:37 | # | 引用
blue 说:
看了命令篇和实践篇,作者思路清晰,解释到位。很好的入门材料,受益了。
2016年11月 5日 23:57 | # | 引用
Passenger22222 说:
shutdown -h now
2016年12月15日 19:44 | # | 引用
tengent 说:
Quote“这是因为开机时,Systemd只执行/etc/systemd/system目录里面的配置文件。”
这句话貌似说的不太正确吧, /etc/systemd/system是优先执行,而不是只执行吧。
reference:https://www.freedesktop.org/wiki/Software/systemd/FrequentlyAskedQuestions/
Q: I want to change a service file, but rpm keeps overwriting it in /usr/lib/systemd/system all the time, how should I handle this?
A: The recommended way is to copy the service file from /usr/lib/systemd/system to /etc/systemd/system and edit it there. The latter directory takes precedence over the former, and rpm will never overwrite it. If you want to use the distributed service file again you can simply delete (or rename) the service file in /etc/systemd/system again.
2017年3月21日 11:37 | # | 引用
花承赞 说:
稳得,Arch wiki 上也是说 /etc/systemd/system 优先级高
2017年6月22日 16:47 | # | 引用
Mr.L 说:
赞!浅显易懂。我怎么就写不出!
2017年11月14日 22:30 | # | 引用
lpf 说:
写的很好,很详细,主要写出了linux中操作的本质(对文件的编辑)
2018年2月27日 16:55 | # | 引用
wing 说:
只是执行是针对/var/lib/systemd/system说的,就是说,尽管这里面也有,但系统只是执行/etc/...../syetem下有链接的。
2018年5月 1日 11:46 | # | 引用
DPnice 说:
大佬们 systemctl 把 springboot 项目 搞成 服务 ,springboot项目里配置了 日志 ,但是用systemctl 启动 日志 没有创建 是什么原因
我写了一个 创建 文件 的 脚本 然后 用 systemctl 执行 创建不了
2018年11月15日 16:31 | # | 引用
zzk 说:
写的很详细。棒!
2019年2月15日 14:38 | # | 引用
阿胜4K 说:
太强了!全网最直观systemd教程,我看了几遍。
2019年2月19日 11:37 | # | 引用
疯琴 说:
上面配置文件中,RemainAfterExit字段设为yes,表示进程退出以后,服务仍然保持执行。这样的话,一旦使用systemctl stop命令停止服务,ExecStop指定的命令就会执行,从而重新开启触摸板。
这段没看懂,systemctl stop以后ExecStop制定的命令就是关闭触摸板,为啥会重新开启?
2019年2月19日 14:48 | # | 引用
yoyo 说:
怎么注释掉一个配置行?
2019年3月23日 14:06 | # | 引用
无名氏 说:
其实我也没太懂,这里有解释,其实我也没太懂:https://stackoverflow.com/questions/38072849/when-should-the-option-remainafterexit-needs-to-be-set-true-when-creating-new-sy
这里也有个例子:https://www.shellhacks.com/systemd-service-file-example/,这个例子的意思是有些一次性执行的命令,比如清空/tmp目录,但它又是后面程序的依赖,就是说后面的程序必须等它清空后才能执行,否则会报错,比如有些.sock文件要清除(我的nginx就是这样),但是因为它是一次性命令,这决定了它不可能保持运行,但后面的程序又要它为active才可以,所以用RemainAfterExit=yes来让后面依赖它的程序认为它还处于运行状态,事实上它并没有在运行,但它确实已经做过清理/tmp目录的工作了。我不知道我说的这样你明白不?
但我还是希望阮老师能解释一下,因为我认为你说的这句话是有问题的。
2019年4月 3日 15:30 | # | 引用
无名氏 说:
我又看了一下,又懂一点了,你要看清楚,它是:/usr/bin/touchpad-off,也就是说:/usr/bin/touchpad-off start 就是关闭,而/usr/bin/touchpad-off stop反而是开启(stop off就是停止关闭,那不就是打开的意思么?双重否定哈哈,差点被忽悠了)
至于RemainAfterExit=yes 表示程序退出后依然保持执行,我的理解是,因为这本身就是oneshot命令,也就是一次性的,它掉就是关掉了,而且除非你重新开启,否则肯定是“保持关掉”的状态,但是stop命令不知道呀,你执行stop的前提是systemctl status看到的状态是“active”状态,你才可以stop,否则你本身就是stop状态的话,是无法执行stop的,所以才需要RemainAfterExit=yes 来让systemctl认为它处于start状态,然后你才能stop。
前面的评论错怪阮老师了,不好意思啊!不过这里确实不是很好理解,我次我认为我的理解是没错的了!
2019年4月 3日 15:44 | # | 引用
xtzt 说:
思路非常清晰!!!
讲的寻寻渐进,环环相扣!!!
不错!!!
2019年8月 6日 13:31 | # | 引用
yuzhewo 说:
如何填写自己的配置,例如程序的端口指定或者配置文件路径指定。
2019年8月15日 11:21 | # | 引用
梦凡 说:
请教一个问题,如果启动tgt服务前,要求目录/mnt/share被挂载,那么在tgt.service中应该增加哪些内容?
2020年4月30日 16:49 | # | 引用
北方的牛 说:
大神请教一下,ARM-Linux中,采用systemd启动,如何开机打开psplash进度条。
2020年8月25日 14:30 | # | 引用
bo 说:
有没有人遇到过拔网线后systemctl启动程序会被关闭,Java -jar启动却不会呢?
2020年9月25日 14:44 | # | 引用
张翠山 说:
谢谢,博主。基本解决了Systemd的困惑。
2021年3月13日 14:05 | # | 引用
nlznl 说:
老大,这俩 /lib/systemd/system 、/usr/lib/systemd/system 好像是一样的,有啥区别呀
2021年3月21日 00:39 | # | 引用
sibey 说:
谢谢大神,清晰明了,一直不知道怎么写配置文件的,现在会了
2021年4月 4日 16:45 | # | 引用
Tz 说:
谢谢大神,写到非常全面。现在遇到一个问题,系统reboot,偶尔出现重启失败,adb 和串口都不能用,不知道怎么定位问题。有什么好方法吗,不知道卡在那个地方了?
2021年8月30日 17:20 | # | 引用
ysyy 说:
写的很清晰,受益良多,感谢阮老师。
2021年12月 7日 11:03 | # | 引用
李 说:
@Yiutto:
进一下主板bios,把操作系统类型换成linux,没有的话就选andriod,应该就能解决问题
如果只是关机超级慢的话,改一下下边这个配置看看能不能解决问题
sudo vim /etc/systemd/system.conf
#DefaultTimeoutStartSec=10s
#DefaultTimeoutStopSec=10s
2022年3月 2日 14:10 | # | 引用
李 说:
新版本的ubuntu都已经不用init.d了,那么新安装的ubuntu18.04.6里还是有/etc/init.d目录的,而且其中也有不少脚本,使用apt安装的mysql-server也会在/etc/init.d中有一个mysql脚本。那么systemd会去启动/etc/init.d中的脚本吗,如果会的话,是哪个service负责去启动呢
2022年3月 2日 16:20 | # | 引用
shooke 说:
关于before和after的描述,作者的描述比较绕。
before是指,在before对应的unit之前启动。
after是指,在after对应unit之后启动。
比如before=syslog.target 指在syslog.target之前启动。after=syslog.target指在syslog.target之后启动
2024年4月30日 17:28 | # | 引用