Linux的Syetemd服务
背景
在Linux操作系统中,系统会首先启动init daemon (pid = 1)
做为第一个进程,其以守护进程的形式在系统中运行,直到系统关闭。守护进程一种在后台执行,而不由用户直接交互控制的程序。init daemon
可以作为其他所有进程的父进程,来管理所有其他进程的启动。
在Centos7
之前,是通过Init
架构(又被称为System V Init或SysVinit)来实现init daemon
如Centos6
,而现在Centos
系统一般都是以Systemd
架构来实现init daemon
。我们可以通过pstree -p
命令来查看进程树。
yum -y install psmisc |
我使用的是centos7.9
,因此systemd(1)
是第一个进程,作为所有其他进程的父进程。因为最近在学习中看到了相关的命令,所以在这里学习记录一下。
Init架构
系统的运行级别
在Init
架构中存在7
个运行级别:
级别 | 功能 |
---|---|
0 |
Shutdown(关机) |
1 |
Single User Mode(单用户模式) |
2 |
Multiuser mode without networking(断网多用户模式) |
3 |
Multiuser mode with networking(联网多用户模式)默认 |
4 |
Unused(未使用) |
5 |
Multiuser mode with networking and GUI(图形化界面的联网多用户模式) |
6 |
Reboot(重启) |
配置文件/etc/inittab
决定以哪种方式运行系统,它是系统在启动init daemon
时读取的第一个文件,默认是以3
来启动。当然对于Centos7
来说,这个文件已经没有用了,文件内容:
[root@www ~]# cat /etc/inittab |
Init架构的服务
在Centos6
,可以通过service
命令来启动相应的服务
service network restart |
在/etc/rc.d/init.d
存放着能够通过service
命令管理的服务,通过按照相应的规则编写好对应的脚本,放到/etc/rc.d/init.d
里面,就可以通过service
命令来管理。在/etc/rc.d/
文件夹里面,还存在/etc/rc.d/rc{0..6}.d
文件夹,其中数字就是对应于Init
架构中的7
个级别,当系统以相应的级别开机或关机的时候,就会直接启动对应那个级别的文件夹里面的脚本。在/etc
下存在软链接指向这几个目录:
[root@www etc]# ll init.d rc{0..6}.d rc.local -d |
我们以级别3
为例,查看相应的文件:
[root@www rc3.d]# pwd |
可以看到这里面的文件,其实/etc/rc.d/init.d
中脚本的软链接,并且名字也有所不同,原因在于Init
架构在执行相应的脚本时,是根据脚本的名字来执行的即:
- S:即Start,系统开机时执行
- K:即Kill,系统关机时执行
- 数字:执行的顺序
- 最后才是脚本的名字
虽然自从Centos7
后,就有Systemd
架构来代替Init
架构,但是在Centos7
中依然兼容了Init
架构。
Init架构的启动
当我们打开给Linux系统的电脑开机的时候,BIOS 会从非易失性存储器中加载,并执行上电自检,之后BIOS会检测系统资源,然后会查找硬盘的主引导记录(MBR),MBR会初始化引导程序如GRBU,GRUB加载内核,内核会执行/sbin/init
程序,其就会成为Pid = 1
的所有Linux进程的父进程,然后/sbin/init
会读取/etc/inittab
文件,确定操作系统的运行级别,从文件/etc/fstab
里查找分区表信息进行相应的挂载,然后按照运行级别执行/etc/rc.d/rcX.d
里面的脚本,按顺序一个一个执行,每次只启动一个服务。关机过程差不多是相反,首先Init
停止所有服务,最后阶段会卸载文件系统。
Systemd架构
一直以来,Centos
都是采用Init
架构,但是Init
架构启动慢,需要串行启动,即进程是一个接着一个启动,只有当前一个进程启动成功,才会去启动下一个进程,同时启动进程的脚本比较复杂,因此Centos7
之后,Systemd
架构取代了Init
架构,其可以做到并行启动,减少系统引导时间和计算开销。同时其为系统的启动和管理提供了完整的解决方案,如下图所示,Systemd
架构及其复杂。
Systemd unit
Systemd
可以管理系统的所有资源,并把这些资源统一称为unit
。对应的unit
有12种。
unit 分类
unit 类型 |
文件后缀 | 描述 |
---|---|---|
Service |
.service |
系统服务,包括启动,重启,关闭服务的相关指令 |
Target |
.target |
定义了一组unit 的集合,一般作为unit 启动的同步点,某个Target 启动成功对应着一组相关的Service 启动成功 |
Automount |
.automount |
系统引导时会进行自动挂载的挂载点 |
Mount |
.mount |
由systemd 管理的文件系统挂载点 |
Path |
.path |
定义了一条用于基于路径激活服务的文件路径。例如,可以基于某一条文件路径的状态(是否存在等)来启动某个服务。 |
Scope |
.scope |
定义了来自systemd 总线接口的信息,通常用来管理额外的系统进程 |
Slice |
.slice |
定义了资源限额,基于Linux cgroup nodes 实现。 |
Snapshot |
.snapshot |
当前的systemd 状态,通常在对systemd 做修改后回滚使用 |
Socket |
.socket |
定义了进程间通信使用的socket ,往往socket 会与service 相关联 |
Swap |
.swap |
定义了系统中的交换空间 |
Timer |
.timer |
定义了一个定时激活另一个单元的定时器 |
unit 的存放路径
路径 | 说明 |
---|---|
/etc/systemd/system |
由systemctl enable 命令创建的systemd unit 文件将存放在此处,实际上是指向/usr/lib/systemd/system 的软链接。系统管理员自定义的unit 也存放在该目录下。默认在这里读取文件 |
/run/systemd/system |
服务运行时产生的相关unit 都存放在这个目录下 |
/usr/lib/systemd/system |
系统或第三方软件安装时添加的unit 放在这个目录下 |
常用的unit
可以利用man 5 systemd.unit类型
去查看帮助文档,如man 5 systemd.slice
,里面包括怎么写配置文件。这里简单介绍一下常用的unit
,包括Service
和Target
。
Service 简单介绍
Service
类型是最常用的unit
,主要是用来启动像httpd
和sshd
这样的服务的配置文件,对应的文件名是httpd.service
和ssh.d.service
,可以使用systemctl cat sshd.service
[root@www etc]# systemctl cat sshd.service |
可以看到比较多字段,接下来我们简单了解一下:# /usr/lib/systemd/system/sshd.service
给出了对应的路径,一个unit
包括三个字段[Unit]
,[Install]
和[Unit类型]
,这里Service
类型对应的就是[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 运行必须满足的条件,否则会报启动失败sshd.service
,Description=OpenSSH server daemon
给出了简单描述,Documentation=man:sshd(8) man:sshd_config(5)
给出了文档信息,After=network.target sshd-keygen.service
表明如果network.target
或sshd-keygen.service
要启动,那么sshd.service
需要在这两个之后启动。Wants=sshd-keygen.service
表明sshd.service
和sshd-keygen.service
有弱依赖关系,即使sshd-keygen.service
未启动,启动失败或者停止服务也不会影响sshd.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、no。no(默认值):退出后不会重启,on-success:只有正常退出时(退出状态码为0),才会重启,on-failure:非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启,on-abnormal:只有被信号终止和超时,才会重启,on-abort:只有在收到没有捕捉到的信号终止时,才会重启,on-watchdog:超时退出,才会重启,always:不管是什么退出原因,总是重启
TimeoutSec:定义 Systemd 停止当前服务之前等待的秒数
Environment:指定环境变量
EnvironmentFile:指定加载一个包含服务所需的环境变量列表的文件,文件中的每一行都是一个环境变量的定义
KillMode:定义 Systemd 如何停止 sshd 服务。可以设置为:control-group(默认值):当前控制组里面的所有子进程,都会被杀掉,process:只杀主进程,mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号,none:没有进程会被杀掉,只是执行服务的 stop 命令。sshd.service
,Type=notify
表明当前服务启动完毕,会通知Systemd
,再继续往下执行,EnvironmentFile=/etc/sysconfig/sshd
从配置文件/etc/sysconfig/sshd
中获取环境变量,最重要的就是这个执行命令ExecStart=/usr/sbin/sshd -D $OPTIONS
,重启当前服务时执行的命令为ExecReload=/bin/kill -HUP $MAINPID
,KillMode=process
表示只停止主进程,不停止任何sshd
子进程,即子进程打开的SSH session
仍然保持连接。这个设置不太常见,但对sshd
很重要,否则你停止服务的时候,会连自己打开的SSH session
一起杀掉。Restart=on-failure
表明出现任何意外的失败都重启,RestartSec=42s
表明在重启sshd
服务之前要等待42s
。[Install]
用来定义如何启动,以及是否开机启动对于WantedBy:它的值是一个或多个 Target,当前 unit 设置为开机启动时,软链接会放入/etc/systemd/system目录下面以 Target 名 + .wants后缀构成的子目录中,表示该服务所在的Target
RequiredBy:它的值是一个或多个 Target,当前 unit 开机启动时,软链接会放入/etc/systemd/system目录下面以 Target 名 + .required后缀构成的子目录中
Alias:当前 unit 可用于启动的别名
Also:当前 unit 激活(enable)时,会被同时激活的其他 unitsshd.service
,WantedBy=multi-user.target
表明它是属于multi-user.target
的,这个很重要,它可以设置开机自动启动,当我们用systemctl enable sshd
时会在/etc/systemd/system/multi-user.target
目录创建软链接到/usr/lib/systemd/system/sshd.service
,Systemd
默认启动的Target
是default.target
,而default.target
是multi-user.target
或graphical.target
的软链接,可以通过systemctl get-default
查询到。当使用命令时,不指定[root@www etc]# systemctl get-default
multi-user.targetunit
类型时,默认是Service
类型,例如systemctl status sshd
,查看服务状态[root@www init.d]# systemctl status sshd
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2024-04-13 16:35:12 CST; 17h ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 933 (sshd)
CGroup: /system.slice/sshd.service
└─933 /usr/sbin/sshd -D
Apr 13 16:35:12 www.a.org systemd[1]: Starting OpenSSH server daemon...
Apr 13 16:35:12 www.a.org sshd[933]: Server listening on 0.0.0.0 port 22.
Apr 13 16:35:12 www.a.org sshd[933]: Server listening on :: port 22.
Apr 13 16:35:12 www.a.org systemd[1]: Started OpenSSH server daemon.
Apr 13 16:35:27 www.a.org sshd[1647]: Accepted password for root from 172.16.34.145 port 2454 ssh2
上面的输出含义:
sshd.service行:服务的基本介绍
Loaded行:配置文件的位置,是否设为开机启动,enabled表示开机自动启动,preset: enabled表示软件安装后默认开机自动启动。
Active行:表示服务的运行状态为正在运行
Docs:文档信息
Main PID:主进程号
CGroup块:应用的子进程
日志块:最下面的带有时间的
Target 简单介绍
Target
就是一个 unit
组,包含许多相关的 unit
。启动某个 Target
的时候,Systemd
就会启动里面所有的 unit
。
[root@www etc]# systemctl cat multi-user.target |
Target
也有自己的配置:# /usr/lib/systemd/system/multi-user.target
对应的目录位置[Unit]
定义了unit
的基本信息。
Description:基本描述 |
对于multi-user.target
,基本描述和文档分别为Description=Multi-User System
和Documentation=man:systemd.special(7)
,Requires=basic.target
表明需要同basic.target
一起运行,Conflicts=rescue.service rescue.target
表明和rescue.service rescue.target
冲突不能一起运行,After=basic.target rescue.service rescue.target
表明如果basic.target和rescue.service rescue.target
要启动,multi-user.target
需要在他们启动之后启动,AllowIsolate=yes
表明允许使用systemctl isolate
命令切换到multi-user.target
在前面介绍了Init
架构的运行级别,在Systemd
架构中也存在类似的概念,主要是通过Target
来定义系统的运行模式,对应的关系如下:
Init 架构运行等级 |
Systemd 架构的Target |
描述 |
---|---|---|
0 |
runlevel0.target -> poweroff.target |
关机 |
1 |
runlevel1.target -> rescue.target |
单用户模式 |
2 |
runlevel2.target -> multi-user.target |
断网多用户模式由联网多用户模式取代 |
3 |
runlevel3.target -> multi-user.target |
联网多用户模式(默认) |
4 |
runlevel4.target -> multi-user.target |
联网多用户模式 |
5 |
runlevel5.target -> graphical.target |
联网多用户模式且带有图形化界面 |
6 |
runlevel6.target -> reboot.target |
重启系统 |
emergency |
emergency.target |
紧急恢复状态 |
Systemd 默认启动的Target 是default.target ,而default.target 是multi-user.target 或graphical.target 的软链接,可以通过systemctl get-default 查询到。 |
[root@www etc]# systemctl get-default |
日志管理
Systemd
统一管理所有Unit
的启动日志。我们可以只用journalctl
一个命令,查看所有日志(内核日志和应用日志),其从/var/log/messages
中获取日志信息,存放在/run/log/journal/
下。配置文件是/etc/systemd/journald.conf
Systemd 的启动
当我们打开给Linux系统的电脑开机的时候,BIOS 会从非易失性存储器中加载,并执行上电自检,之后BIOS会检测系统资源,然后会查找硬盘的主引导记录(MBR),MBR会初始化引导程序如GRBU或者LILO。
MBR从GRBU或LILO引导程序读取相关信息并初始化内核。内核执行
Systemd
,之后的启动就交给Systemd
了。Systemd
使用Target
来处理引导和服务管理过程。Target
文件被用于分组不同的引导单元以及启动同步进程。Systemd
执行的第一个Target
是default.target
,并递归的处理它的依赖关系,default.target
实际上是指向multi-user.target
或者graphical.target
的软链接。[root@www ~]# systemctl cat default.target
/usr/lib/systemd/system/multi-user.target
This file is part of systemd.
# systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
[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根据依赖关系,需要先启动
sysinit.target,basic.target
来初始化系统。multi-user.target
会启动/etc/systemd/system/multi-user.target.wants
下的服务,为多用户支持设定系统环境。非root用户会在这个阶段的引导过程中启用。防火墙相关的服务也会在这个阶段启动。利用这一点可以使得一些服务开机自动启动Systemd
是可以兼容Init
架构中的rc.local
配置的,通过rc-local.service
来实现兼容的,Systemd
在启动的很早就会判断/etc/rc.local
是否存在并且是可执行的,如果满足条件,那么Systemd
会调用/usr/lib/systemd/system-generators/
下的程序,把rc-local.service
服务加入到default.target
中来。这样在后面的执行时就会触发rc.local
的运行,利用这一点我们可以实现程序的开机自动启动。#################################### 启动流程图 #######################################
cryptsetup-pre.target veritysetup-pre.target
|
(various low-level v
API VFS mounts: (various cryptsetup/veritysetup devices...)
mqueue, configfs, | |
debugfs, ...) v |
| cryptsetup.target |
| (various swap | | remote-fs-pre.target
| devices...) | | | |
| | | | | v
| v local-fs-pre.target | | | (network file systems)
| swap.target | | v v |
| | v | remote-cryptsetup.target |
| | (various low-level (various mounts and | remote-veritysetup.target |
| | services: udevd, fsck services...) | | |
| | tmpfiles, random | | | remote-fs.target
| | seed, sysctl, ...) v | | |
| | | local-fs.target | | _____________/
| | | | | |/
\____|______|_______________ ______|___________/ |
\ / |
v |
sysinit.target |
| |
______________________/|\_____________________ |
/ | | | \ |
| | | | | |
v v | v | |
(various (various | (various | |
timers...) paths...) | sockets...) | |
| | | | | |
v v | v | |
timers.target paths.target | sockets.target | |
| | | | v |
v \_______ | _____/ rescue.service |
\|/ | |
v v |
basic.target rescue.target |
| |
________v____________________ |
/ | \ |
| | | |
v v v |
display- (various system (various system |
manager.service services services) |
| required for | |
| graphical UIs) v v
| | multi-user.target
emergency.service | | |
| \_____________ | _____________/
v \|/
emergency.target v
graphical.target关于Linux的开机启动可以参考:Linux的启动过程
Systemd 相关的命令
系统相关
## systemd ### |
unit相关
man 5 systemd.slice #查看某种类型unit的文档 |
日志相关
journalctl #查看日志,默认只保存本次启动的日志 |
systemd-
通过在终端输入systemd-
,然后Tab补全就可以查看相应的命令,这些命令是systemd
架构提供的可用命令,平时很少用到。
创建自己的Service
- 创建配置文件
vim /etc/systemd/system/hello.service
### 文件内容
[Unit]
Description=Hello
[Service]
ExecStart=/usr/bin/python /root/hello.py
Restart=on-failure
[Install]
WantedBy=multi-user.target - 创建
python
脚本文件vim /root/hello.py
### 脚本内容
import logging
import time
logging.basicConfig(format='Date-Time : %(asctime)s : Line No. : %(lineno)d - %(message)s', level = logging.DEBUG)
while True:
logging.debug("A Debug logging Message")
time.sleep(10) - 启动服务,并设置开机自启
systemctl daemon-reload # 编写后需要重新加载新的配置文件
systemctl start hello.service #启动服务
systemctl enable hello.service #设置开机自动启动
systemctl status hello.service #查看状态
[root@www ~]# systemctl status hello.service -l
● hello.service - Hello
Loaded: loaded (/etc/systemd/system/hello.service; enabled; vendor preset: disabled)
Active: active (running) since Sun 2024-04-14 11:27:37 CST; 2min 17s ago
Main PID: 8128 (python)
CGroup: /system.slice/hello.service
└─8128 /usr/bin/python /root/hello.py
Apr 14 11:28:17 www.a.org python[8128]: Date-Time : 2024-04-14 11:28:17,540 : Line No. : 5 - A Debug logging Message
Apr 14 11:28:27 www.a.org python[8128]: Date-Time : 2024-04-14 11:28:27,549 : Line No. : 5 - A Debug logging Message
Apr 14 11:28:37 www.a.org python[8128]: Date-Time : 2024-04-14 11:28:37,580 : Line No. : 5 - A Debug logging Message
Apr 14 11:28:47 www.a.org python[8128]: Date-Time : 2024-04-14 11:28:47,612 : Line No. : 5 - A Debug logging Message
Apr 14 11:28:57 www.a.org python[8128]: Date-Time : 2024-04-14 11:28:57,619 : Line No. : 5 - A Debug logging Message
Apr 14 11:29:07 www.a.org python[8128]: Date-Time : 2024-04-14 11:29:07,638 : Line No. : 5 - A Debug logging Message
Apr 14 11:29:17 www.a.org python[8128]: Date-Time : 2024-04-14 11:29:17,642 : Line No. : 5 - A Debug logging Message
Apr 14 11:29:27 www.a.org python[8128]: Date-Time : 2024-04-14 11:29:27,701 : Line No. : 5 - A Debug logging Message
Apr 14 11:29:37 www.a.org python[8128]: Date-Time : 2024-04-14 11:29:37,728 : Line No. : 5 - A Debug logging Message
Apr 14 11:29:47 www.a.org python[8128]: Date-Time : 2024-04-14 11:29:47,737 : Line No. : 5 - A Debug logging Message
systemctl restart hello #重启服务
ps aux | grep hello #查看进程
[root@www ~]# ps aux | grep hello
root 8347 0.0 0.2 138728 5404 ? Ss 11:31 0:00 /usr/bin/python /root/hello.py
root 8373 0.0 0.0 112812 976 pts/0 S+ 11:31 0:00 grep --color=auto hello
systemctl stop hello #停止服务