图源:
daemon与service
daemon可以直译为守护进程,在Linux中特指服务进程。
在Linux中,这两者一般可以简单地等同,都可以指代服务进程,不过一般会使用daemon这个称呼。
关于
daemon
一词的来源和故事可以阅读
systemd
早期的Linux发行版使用System V管理服务进程,但从CentOS 7.x开始,已经普遍使用systemd管理服务。
如果想了解早期的System V的相关内容,可以阅读。
相对于System V,systemd具有以下优点:
-
并发启动服务,加快开机速度
System V对服务是顺序启动,所以开机后会花费较长的时间来启动服务,而systemd则会并发地启动服务,所以开机花费地时间会显著减少。
-
对服务操作的集中响应
System V是使用脚本的方式操作服务,而systemd则本身就是一个常驻进程的服务,并可以通过
systemctl
命令来操作服务,并迅速进行响应。 -
自动检查服务关联性
服务之间往往是有关联性的,比如sshd服务,就依赖于网络相关的服务,而System V中需要手动依次启动相关的服务,systemd则会自动检测服务的关联性,并自动启动相关联的服务。
-
对daemon的详细分类
System V中仅会将daemon分类为stand alone和super daemon,而systemd则将daemon分为service、socket、target、path等多个类型。
-
daemon群组
System V是将系统状态分为多个runlevel,在不同的runlevel中启动不同的服务,而systemd则是通过一个类似作用的“daemon群组”——target来组织和管理daemon,一个target启动后,其中所有的daemon会一同启动。而且一个target可以包含其它target。
-
向下兼容
在基本的功能上,systemd可以兼容System V。
systemd配置文件
systemd的相关配置文件存放于这三个目录:
-
/usr/lib/systemd/system/
:Linux发行版安装后,systemd的相关配置会自动安装在这里,可以看作是systemd的原始配置目录。 -
/run/systemd/system/
:系统运行时,内存中加载的systemd配置。 -
/etc/systemd/system/
:实际使用的配置目录,启用的服务配置会通过创建软链接的方式链接到/usr/lib/systemd/system/
中的相关配置文件,如果用户需要自定义服务或者修改已有的服务配置,都可以在这里创建和修改。
这三个目录的优先级是/etc/systemd/system/
大于/run/systemd/system/
大于/usr/lib/systemd/system/
systemd的unit分类
systemd将配置目录中可以加载的daemon配置文件称作unit,一个配置文件就是一个unit:
[icexmoon@xyz ~]$ ll /usr/lib/systemd/system | grep -E '(vsftpd|multi|cron)'
-rw-r--r--. 1 root root 318 8月 9 2019 crond.service
-rw-r--r--. 1 root root 623 9月 30 2020 multipathd.service
-rw-r--r--. 1 root root 492 10月 2 2020 multi-user.target
drwxr-xr-x. 2 root root 258 7月 24 14:35 multi-user.target.wants
lrwxrwxrwx. 1 root root 17 7月 24 14:35 runlevel2.target -> multi-user.target
lrwxrwxrwx. 1 root root 17 7月 24 14:35 runlevel3.target -> multi-user.target
lrwxrwxrwx. 1 root root 17 7月 24 14:35 runlevel4.target -> multi-user.target
-rw-r--r--. 1 root root 171 6月 10 00:15 vsftpd.service
-rw-r--r--. 1 root root 184 6月 10 00:15 vsftpd@.service
-rw-r--r--. 1 root root 89 6月 10 00:15 vsftpd.target
unit具有不同类型,可以根据后缀名进行划分:
后缀名 | 说明 |
---|---|
.service | 主要服务(service unit),包括Linux正常运行所需的本地服务和网络服务 |
.socket | 内部进程通(Inter-process communication,IPC)信所需的套接字服务(socket unit) |
.target | 运行环境,类似于System-V中的runlevel,就是一组unit的集合。 |
.automount | 文件系统挂载相关的服务(automount unit/ mount unit) |
.mount | 同上 |
.path | 监控特定文件或目录的服务(path unit) |
.timer | 执行计划任务的服务(timer unit),类似于crond的功能 |
通过systemctl管理服务
服务的启动、查看状态
要使用systemctl
查看服务状态很简单:
[icexmoon@xyz ~]$ systemctl status atd
● atd.service - Job spooling tools
Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
Active: active (running) since 三 2021-09-01 11:59:15 CST; 1h 48min ago
Main PID: 1266 (atd)
Tasks: 1
CGroup: /system.slice/atd.service
└─1266 /usr/sbin/atd -f
9月 01 11:59:15 xyz.icexmoon.centos systemd[1]: Started Job spooling tools.
第一行是服务名atd.service
以及服务简介job spooling tools
。
第二行是加载情况:
-
loaded
表示相关配置信息已经被systemd
服务加载 -
/usr/lib/systemd/system/atd.service
是服务的配置文件的实际路径 -
enabled
表示服务会开机自启动,除了enabled
,还会有:-
disabled
:不会开机自启动 -
static
:服务不能自启动,但是可以被其它服务唤醒 -
mask
:服务被注销,无法启动
-
第三行是服务的运行情况:
-
active(running)
表示服务正在运行,其它的运行情况还有:-
active(exited)
:仅执行依次就终止的服务,目前已终止 -
active(waiting)
:正在运行中,不过需要等待其它事件发生才继续运行,比如等待I/O -
inactive
:没有在运行
-
-
since 三 2021-09-01 11:59:15 CST;1h 48min ago
表示的是服务运行的开始时间和持续时间
第四行是服务的主进程PID1266
以及触发主进程的命令atd
。
最后几行的信息为服务的运行日志,服务的启动,停止等日志都会显示在这里。
需要说明的是,systemctl
命令中可以使用服务名,比如上边的systemctl status atd
。或者完整的服务unit名称,比如systemctl status atd.service
。
如果要关闭服务,也很简单:
[icexmoon@xyz ~]$ systemctl stop atd
[icexmoon@xyz ~]$ systemctl status atd
● atd.service - Job spooling tools
Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
Active: inactive (dead) since 三 2021-09-01 14:02:39 CST; 11s ago
Process: 1266 ExecStart=/usr/sbin/atd -f $OPTS (code=exited, status=0/SUCCESS)
Main PID: 1266 (code=exited, status=0/SUCCESS)
9月 01 11:59:15 xyz.icexmoon.centos systemd[1]: Started Job spooling tools.
9月 01 14:02:39 xyz.icexmoon.centos systemd[1]: Stopping Job spooling tools...
9月 01 14:02:39 xyz.icexmoon.centos systemd[1]: Stopped Job spooling tools.
可以看到,最后几行的日志信息中出现了停止服务的相关日志。需要说明的是,这种方法只是暂时关闭服务,因为服务的开机自启动状态依然是enabled
,服务将会在下次开机后重启。
下面再看一个例子:
[root@xyz ~]# systemctl status chronyd
● chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled)
Active: active (running) since 三 2021-09-01 14:07:10 CST; 59s ago
Docs: man:chronyd(8)
man:chrony.conf(5)
Main PID: 3499 (chronyd)
CGroup: /system.slice/chronyd.service
└─3499 /usr/sbin/chronyd
...省略
Hint: Some lines were ellipsized, use -l to show in full.
[root@xyz ~]# systemctl stop chronyd
[root@xyz ~]# systemctl disable chronyd
Removed symlink /etc/systemd/system/multi-user.target.wants/chronyd.service.
[root@xyz ~]# systemctl status chronyd
● chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: man:chronyd(8)
man:chrony.conf(5)
...省略
只有这样,在systemctl stop servername
的同时使用systemctl disable servername
才能完全关闭服务。
此外,在关闭服务自启动的时候,可以看到Removed symlink
的相关信息,这是因为自启动服务其实是通过在所属的target
的配置目录下创建软连接的方式实现的,当所属的target
被执行以后,相关联的服务自然会被启动,而同样的,如果不需要自启动了,只需要将软连接删除即可。
除了普通会自启动的服务以外,有的服务可以被其它服务“唤醒”:
[root@xyz ~]# systemctl status cups.service
● cups.service - CUPS Printing Service
Loaded: loaded (/usr/lib/systemd/system/cups.service; enabled; vendor preset: enabled)
Active: active (running) since 三 2021-09-01 11:59:14 CST; 2h 15min ago
Main PID: 1241 (cupsd)
CGroup: /system.slice/cups.service
└─1241 /usr/sbin/cupsd -f
9月 01 11:59:14 xyz.icexmoon.centos systemd[1]: Started CUPS Printing Service.
[root@xyz ~]# systemctl stop cups.service
Warning: Stopping cups.service, but it can still be activated by:
cups.socket
cups.path
[root@xyz ~]# systemctl disable cups.service
Removed symlink /etc/systemd/system/multi-user.target.wants/cups.path.
Removed symlink /etc/systemd/system/multi-user.target.wants/cups.service.
Removed symlink /etc/systemd/system/sockets.target.wants/cups.socket.
Removed symlink /etc/systemd/system/printer.target.wants/cups.service.
[root@xyz ~]# netstat -tulnp | grep cups
cups
是一个打印服务,会常驻进程并监听端口,这里直接关闭,可以看到在关闭的时候会提示it can still be activated by
,这是再说虽然该进程被关闭了,但依然可以被其它进程唤醒。最后通过netstat
查看后可以发现的确没有cups
进程监听端口了。
下面我们看是否可以被其它服务唤醒:
[root@xyz ~]# systemctl status cups.socket
● cups.socket - CUPS Printing Service Sockets
Loaded: loaded (/usr/lib/systemd/system/cups.socket; disabled; vendor preset: enabled)
Active: active (listening) since 三 2021-09-01 11:59:02 CST; 2h 21min ago
Listen: /var/run/cups/cups.sock (Stream)
9月 01 11:59:02 xyz.icexmoon.centos systemd[1]: Listening on CUPS Printing Service Sockets.
[root@xyz ~]# systemctl status cups.path
● cups.path - CUPS Printer Service Spool
Loaded: loaded (/usr/lib/systemd/system/cups.path; disabled; vendor preset: enabled)
Active: active (waiting) since 三 2021-09-01 11:59:02 CST; 2h 22min ago
9月 01 11:59:02 xyz.icexmoon.centos systemd[1]: Started CUPS Printer Service Spool.
[root@xyz ~]# echo 'testing' | lp
lp: Error - no default destination available.
[root@xyz ~]# systemctl status cups.service
● cups.service - CUPS Printing Service
Loaded: loaded (/usr/lib/systemd/system/cups.service; disabled; vendor preset: enabled)
Active: active (running) since 三 2021-09-01 14:21:47 CST; 12s ago
Main PID: 3827 (cupsd)
Tasks: 1
CGroup: /system.slice/cups.service
└─3827 /usr/sbin/cupsd -f
9月 01 14:21:47 xyz.icexmoon.centos systemd[1]: Started CUPS Printing Service.
[root@xyz ~]# netstat -tulnp | grep cups
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN 3827/cupsd
tcp6 0 0 ::1:631 :::* LISTEN 3827/cupsd
在确保cups.socket
和cups.path
两个服务在运行后,使用lp
命令调用打印功能,可以看到cups.service
服务被“唤醒”了,而且也的确出现了cupsd
进程正在监听631
端口。
注销服务
从上边的示例可看到,有时候关闭单一的服务并不能避免服务再启动,比如说像cups.service
那样会被相关的服务唤醒,除非关闭所有相关的服务。除了这种做法,我们还有一个更省事的选择,即注销服务:
[root@xyz ~]# systemctl stop cups.service
Warning: Stopping cups.service, but it can still be activated by:
cups.path
cups.socket
[root@xyz ~]# systemctl mask cups.service
Created symlink from /etc/systemd/system/cups.service to /dev/null.
[root@xyz ~]# systemctl status cups.service
● cups.service
Loaded: masked (/dev/null; bad)
Active: inactive (dead) since 三 2021-09-01 14:28:15 CST; 44s ago
Main PID: 3827 (code=exited, status=0/SUCCESS)
9月 01 14:21:47 xyz.icexmoon.centos systemd[1]: Started CUPS Printing Service.
9月 01 14:28:15 xyz.icexmoon.centos systemd[1]: Stopping CUPS Printing Service...
9月 01 14:28:15 xyz.icexmoon.centos systemd[1]: Stopped CUPS Printing Service.
[root@xyz ~]# systemctl start cups.service
Failed to start cups.service: Unit is masked.
我们之前说过,systemd使用的配置目录中最高优先级的是/etc/systemd/system
,实际使用中所有默认安装的服务都是通过软链接的形式存在于这个目录下的,所以这里用相当讨巧的方式屏蔽一个服务:用/dev/null
这个没用的软链接代替原有的链接,这样自然就无法使用对应的服务了。
可以看到此时服务的加载状态变为了masked (/dev/null; bad)
,服务的运行状态变成了inactive (dead)
。
如果要从已注销的状态(masked)恢复,也很简单:
[root@xyz ~]# systemctl unmask cups.service
Removed symlink /etc/systemd/system/cups.service.
[root@xyz ~]# systemctl status cups.service
● cups.service - CUPS Printing Service
Loaded: loaded (/usr/lib/systemd/system/cups.service; disabled; vendor preset: enabled)
Active: inactive (dead) since 三 2021-09-01 14:28:15 CST; 6min ago
Main PID: 3827 (code=exited, status=0/SUCCESS)
9月 01 14:21:47 xyz.icexmoon.centos systemd[1]: Started CUPS Printing Service.
9月 01 14:28:15 xyz.icexmoon.centos systemd[1]: Stopping CUPS Printing Service...
9月 01 14:28:15 xyz.icexmoon.centos systemd[1]: Stopped CUPS Printing Service.
查看系统中的所有服务
可以通过systemctl
查看已经被加载的unit:
[root@xyz ~]# systemctl
UNIT LOAD ACTIVE SUB DESCRIPTION
proc-sys-fs-binfmt_misc.automount loaded active waiting Arbitrary Executable File Formats File System Automou
sys-devices-pci0000:00-0000:00:07.1-ata2-host2-target2:0:0-2:0:0:0-block-sr0.device loaded active plugged VMware_Virtual_IDE_CDRO
sys-devices-pci0000:00-0000:00:10.0-host0-target0:0:0-0:0:0:0-block-sda-sda1.device loaded active plugged VMware_Virtual_S 1
sys-devices-pci0000:00-0000:00:10.0-host0-target0:0:0-0:0:0:0-block-sda-sda2.device loaded active plugged VMware_Virtual_S 2
sys-devices-pci0000:00-0000:00:10.0-host0-target0:0:0-0:0:0:0-block-sda-sda3.device loaded active plugged VMware_Virtual_S 3
sys-devices-pci0000:00-0000:00:10.0-host0-target0:0:0-0:0:0:0-block-sda-sda4.device loaded active plugged VMware_Virtual_S Li
...省略
输出的几个字段都很好理解,其中SUB
是和ACTIVE
紧密关联的,合起来就是之前我们在systemctl status servername
中看到的运行状态信息。
默认systemctl
输出的是已经加载的unit
,等同于systemctl list-units
,如果要查看系统中已经安装了哪些服务:
[root@xyz ~]# systemctl list-unit-files
UNIT FILE STATE
proc-sys-fs-binfmt_misc.automount static
dev-hugepages.mount static
dev-mqueue.mount static
proc-fs-nfsd.mount static
proc-sys-fs-binfmt_misc.mount static
run-vmblock\x2dfuse.mount disabled
sys-fs-fuse-connections.mount static
sys-kernel-config.mount static
sys-kernel-debug.mount static
tmp.mount disabled
...省略
如果要按类型筛选,比如只想看已安装的*.service
类型的unit,可以:
[root@xyz ~]# systemctl list-units --type=service --all
UNIT LOAD ACTIVE SUB DESCRIPTION
abrt-ccpp.service loaded active exited Install ABRT coredump hook
abrt-oops.service loaded active running ABRT kernel log watcher
abrt-vmcore.service loaded inactive dead Harvest vmcores for ABRT
abrt-xorg.service loaded active running ABRT Xorg log watcher
abrtd.service loaded active running ABRT Automated Bug Reporting Tool
accounts-daemon.service loaded active running Accounts Service
alsa-restore.service loaded inactive dead Save/Restore Sound Card State
alsa-state.service loaded active running Manage Sound Card State (restore and store)
● apparmor.service not-found inactive dead apparmor.service
atd.service loaded inactive dead Job spooling tools
...省略
这里--all
参数的用途是现实全部的unit,无论是否被加载和运行。
当然可以结合其它管线命令进一步筛选:
[root@xyz ~]# systemctl list-units --type=service --all | grep cpu
cpupower.service loaded inactive dead Configure CPU power related settings
管理不同的操作环境
之前我们说过,在systemd中,unit按照target进行组织,不同的target代表不同的系统状态,类似于老式System V的runlevel。
我们可以使用systemctl
查看当前有哪些target:
[root@xyz ~]# systemctl list-units --type=target --all
UNIT LOAD ACTIVE SUB DESCRIPTION
basic.target loaded active active Basic System
bluetooth.target loaded active active Bluetooth
cryptsetup.target loaded active active Local Encrypted Volumes
emergency.target loaded inactive dead Emergency Mode
final.target loaded inactive dead Final Step
getty-pre.target loaded active active Login Prompts (Pre)
getty.target loaded active active Login Prompts
...省略
这其中与操作界面相关的target有这几个:
-
graphical.target
:这个代表命令行+图形界面,并且其中包含了multi-user.target
-
multi-user.target
:纯命令行界面 -
rescue.target
:在无法使用root
登录系统的时候,可以使用这个模式启动,这个模式是一个和原本系统没关系的启动模式,所以可以使用该模式下的root
修复原系统。 -
emergency.target
:紧急修复模式,如果不能使用rescue.target
,可以使用该模式尝试修复。 -
shutdown.target
:关机 -
getty.target
:负责启动多个tty
终端
我们都知道,对于Linux来说图形界面并非是一定需要的东西,而如果我们在安装系统的时候安装的是图形桌面版本,则开机后会自动进入命令行+图形界面这个模式,也就是graphical.target
,那可不可以切换到纯命令行呢:
[root@xyz ~]# systemctl get-default
graphical.target
[root@xyz ~]# systemctl set-default multi-user.target
Removed symlink /etc/systemd/system/default.target.
Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/multi-user.target.
[root@xyz ~]# systemctl get-default
multi-user.target
这样做可以让系统的“默认模式”从图形+命令行变为纯命令行,但是这种修改的效果需要系统重启后才能看到。
如果我们要立即切换到纯命令行模式:
[root@xyz ~]# systemctl isolate multi-user.target
此时如果查看虚拟机,可以发现已经从图形界面变成了命令行模式:
不用担心,要切换回来也很简单:
[root@xyz ~]# systemctl isolate graphical.target
从一个
target
切换到另一个target
不能使用类似systemctl start graphical.target
的做法,必须使用systemctl isolate graphical.target
。isolate
的含义是“使隔离、使脱离”,用在这里是让系统模式从一个target
脱离后变为另一个target
的意思,而且突出了不同模式之间是隔离和独立的。
除了上边切换模式的用法以外,对于target
的使用,systemctl
还提供了一些“快捷”操作:
-
systemctl poweroff
:关机 -
systemctl reboot
:重启 -
systemctl suspend
:进入挂起模式 -
systemctl hibernate
:进入休眠模式 -
systemctl rescue
:强制进入救援模式 -
systemctl emergency
:强制进入紧急救援模式
其中suspend
和hibernate
比较有意思,对应到Windows应该就是睡眠和深度睡眠模式了。前者是系统会关闭绝大多数硬件的同时保持内存通电,从而在一个低功耗的情况下保持当前的工作环境,并且可以快速响应键盘和电源键等进行“快速唤醒”,是一个相当棒的功能,缺点就是对于笔记本来说要注意电池剩余电量,因为本质上依然需要给内存供电,如果彻底没电就会丢失数据。后者则是将当前工作环境数据从内存写入到硬盘,然后关闭绝大多数硬件,相当于关机,不过比关机好的地方在于再次唤醒电脑后会自动从硬盘加载数据到内存,会让你的工作环境恢复到“深度睡眠”之前的状态,这样就会省去每次开机后打开之前的文档等一些操作,这个功能相当不错。缺点是因为要从硬盘加载,所以唤醒时间较长,相当于重新开机了。
这也正是为什么MacBook号称从不关机,其实就是使用的“深度睡眠”的功能,毕竟都是UNIX-like系统。反观Windows,基本上不是很推深度睡眠的功能,因为系统本身就很有一些问题,健壮性不是UNIX-like系统可比的,如果经常使用深度睡眠不关机,那系统不蓝屏个几次才怪...
服务依赖
之前说过,在systemd中服务之间是由依赖关系的,而同样作为unit
的target
,之间同样也存在依赖关系:
[root@xyz ~]# systemctl get-default
multi-user.target
[root@xyz ~]# systemctl list-dependencies
default.target
● ├─abrt-ccpp.service
● ├─abrt-oops.service
● ├─abrt-vmcore.service
● ├─abrt-xorg.service
● ├─abrtd.service
● ├─atd.service
● ├─auditd.service
● ├─avahi-daemon.service
● ├─crond.service
● ├─dbus.service
● ├─firewalld.service
● ├─initial-setup-reconfiguration.service
默认情况下systemctl list-dependencies
输出的是默认模式的依赖关系,也就是multi-user.target
。
如果想查看multi-user.target
会被哪些target
包含,可以:
[root@xyz ~]# systemctl list-dependencies --reverse
default.target
● └─graphical.target
--reverse
参数是反向查找关联关系的意思。
如果想看graphial.target
关联了哪些服务和target
,可以:
[root@xyz ~]# systemctl list-dependencies graphical.target
graphical.target
● ├─accounts-daemon.service
● ├─gdm.service
● ├─initial-setup-reconfiguration.service
● ├─network.service
● ├─rtkit-daemon.service
● ├─systemd-update-utmp-runlevel.service
● ├─udisks2.service
● └─multi-user.target
● ├─abrt-ccpp.service
● ├─abrt-oops.service
● ├─abrt-vmcore.service
● ├─abrt-xorg.service
...省略
很清楚地看到multi-user.target
是被关联在graphical.target
下面的。
socket
之前有说过,Linux主机的本地进程会通过socket文件进行通信,systemd管理的服务同样也是如此,如果要查看systemd中用到的socket文件,可以:
[root@xyz ~]# systemctl list-sockets
LISTEN UNIT ACTIVATES
/dev/log systemd-journald.socket systemd-journald.service
/run/dbus/system_bus_socket dbus.socket dbus.service
/run/dmeventd-client dm-event.socket dm-event.service
/run/dmeventd-server dm-event.socket dm-event.service
/run/lvm/lvmetad.socket lvm2-lvmetad.socket lvm2-lvmetad.service
/run/lvm/lvmpolld.socket lvm2-lvmpolld.socket lvm2-lvmpolld.service
/run/systemd/initctl/fifo systemd-initctl.socket systemd-initctl.service
/run/systemd/journal/socket systemd-journald.socket systemd-journald.service
/run/systemd/journal/stdout systemd-journald.socket systemd-journald.service
/run/systemd/shutdownd systemd-shutdownd.socket systemd-shutdownd.service
/run/udev/control systemd-udevd-control.socket systemd-udevd.service
/var/run/avahi-daemon/socket avahi-daemon.socket avahi-daemon.service
/var/run/libvirt/virtlockd-sock virtlockd.socket virtlockd.service
/var/run/libvirt/virtlogd-sock virtlogd.socket virtlogd.service
/var/run/rpcbind.sock rpcbind.socket rpcbind.service
@ISCSIADM_ABSTRACT_NAMESPACE iscsid.socket iscsid.service
@ISCSID_UIP_ABSTRACT_NAMESPACE iscsiuio.socket iscsiuio.service
kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
18 sockets listed.
Pass --all to see loaded but inactive sockets, too.
三列分别代表了socket
文件路径、socket
服务以及service
服务。
此外,对于web服务,通常有一些约定的服务和对应的端口(比如ssh
对应21端口等),在Linux中有一个配置文件保存这些信息:
[root@xyz ~]# cat /etc/services | grep -E '(ssh|http)'
# http://www.iana.org/assignments/port-numbers
ssh 22/tcp # The Secure Shell (SSH) Protocol
ssh 22/udp # The Secure Shell (SSH) Protocol
http 80/tcp www www-http # WorldWideWeb HTTP
http 80/udp www www-http # HyperText Transfer Protocol
http 80/sctp # HyperText Transfer Protocol
https 443/tcp # http protocol over TLS/SSL
https 443/udp # http protocol over TLS/SSL
https 443/sctp # http protocol over TLS/SSL
...省略
需要注意的是这里也只是一个默认的服务和端口对应信息,实际使用中是可以修改服务使用的端口的,所以具体还是要通过netstat
查看实际监听的端口是哪个。
关闭网络服务
有时候系统中可能存在一些用不到的网络服务:
[root@xyz ~]# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...省略
udp 0 0 0.0.0.0:46225 0.0.0.0:* 796/avahi-daemon: r
udp 0 0 0.0.0.0:5353 0.0.0.0:* 796/avahi-daemon: r
udp 0 0 0.0.0.0:908 0.0.0.0:* 733/rpcbind
udp6 0 0 :::111 :::* 733/rpcbind
udp6 0 0 :::908 :::* 733/rpcbind
我们查一查这里的avahi-daemon
,看看这个服务是干嘛用的:
[root@xyz ~]# systemctl list-units --all | grep avahi-daemon
avahi-daemon.service loaded active running Avahi mDNS/DNS-SD Stack
avahi-daemon.socket loaded active running Avahi mDNS/DNS-SD Stack Activation Socket
mDNS
是一个局域网内路由的功能(详细说明见),暂时用不到,关闭掉:
[root@xyz ~]# systemctl stop avahi-daemon.service avahi-daemon.socket
[root@xyz ~]# systemctl disable avahi-daemon.service avahi-daemon.socket
Removed symlink /etc/systemd/system/multi-user.target.wants/avahi-daemon.service.
Removed symlink /etc/systemd/system/sockets.target.wants/avahi-daemon.socket.
Removed symlink /etc/systemd/system/dbus-org.freedesktop.Avahi.service.
[root@xyz ~]# netstat -utlnp | grep avahi
service unit的配置文件
之前说过,服务安装以后,相关的配置文件会安装到/usr/lib/systemd/system/
目录下,比如对于vsftpd
这个服务,其原始的配置文件就是/usr/lib/systemd/system/vsftpd.service
。
如果我们需要对原始配置进行修改,最好不好直接在原始配置文件上修改,而是应该在/etc/systemd/system/vsftpd.service.d/
目录下创建自定义配置文件,文件名可以使用custom.conf
这样的,以.conf
作为后缀名。
此外,如果有关联的“前置服务”,可以用软链接的方式链接到/etc/systemd/system/vsftpd.service.requires/
目录,如果有服务启动完毕后需要启动的服务,可以用软链接的方式链接到/etc/systemd/system/vsftpd.service.wants/
目录下。
配置文件的配置项
unit配置文件中有很多配置项,这里以sshd.service
这个配置文件进行说明:
[icexmoon@xyz ~]$ cat /usr/lib/systemd/system/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]
Type=notify
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target
有以下配置项:
-
[Unit]
:unit
本身的相关说明,以及“前置”和“后置”服务说明。 -
[Service]
:不同的类型的unit
会对应不同的配置项,如Socket
、Timer
、Path
等。这个配置项会包含环境变量文件,启动项等。 -
[Install]
:用来说明当前unit
会安装到哪个target
中。
配置文件还有这些规则:
-
配置项可以重复,且后设置的会覆盖先前设置的,如果要清空一个配置项,可以使用
After=
这样的写法。 -
如果配置项是bool类型,可以使用
1
、yes
、true
、on
表示开启,用0
、no
、false
、off
表示关闭。 -
#
或;
开头可以表示注释。
个人建议不要使用重复配置项,以引起混淆,对不需要的配置项可以注释。
下面对配置项进行详细说明:
[Unit]
-
Description
:服务简介,在systemctl status
和systemctl list-units
中看到的内容都来自这里。 -
Documentation
:可以提供进一步的服务说明,形式可以是下面的一种:-
Documentation=http://www...
-
Documentation=man:sshd(8)
-
Documentation=file:/etc/ssh/ssd_config
-
-
After
:在哪些服务之后才启动,并不具有强制性。 -
Before
:在哪些服务之前启动,并不具有强制性。 -
Requires
:前置服务,具有强制性,一定要都启动后才能启动当前服务。 -
Wants
:当前服务启动后,应当再启动哪些服务,不具备强制性。 -
Conflicts
:与哪些服务有冲突,即互相冲突的服务同时仅能有一个运行。
[Service]
-
Type
:服务的启动方式,包括下面几种:-
simple
:默认值,由ExecStart
设置的命令启动主进程。 -
forking
:以fork的方式从父进程创建子进程,创建后父进程会立即退出。httpd
服务就是采用这种方式。 -
oneshot
:一次性进程,systemd会等当前服务退出后再继续执行 -
dbus
:当前服务通过D-Bus启动 -
notify
:服务启动完毕后会通知systemd,然后再继续执行 -
idle
:其它服务都执行完毕后当前服务才会运行
-
-
EnvironmentFile
:指定服务的环境变量设置文件 -
ExecStartPre
:服务启动前执行的命令或脚本 -
ExecStart
:启动服务的命令或脚本,仅支持简单的command parameter1 parameter2
这样的写法,不支持< > |
等复杂的bash语法,如果要执行复杂的命令,可以使用shell脚本或者sh -c 'long command'
。 -
ExecStartPost
:服务启动后执行的命令或脚本 -
ExecStop
:关闭服务(systemctl stop
)时执行的命令 -
ExecReload
:重新记载服务(systemctl reload
)时执行的命令 -
Restart
:定义何种情况下systemd会重新启动服务,可能的值包括:always
、on-success
、on-failure
、on-abnormal
、on-abort
、on-watchdog
。 -
RemainAfterExit
:当设置RemainAfterExit=1
时,当前服务的所有进程都终止后,服务将尝试重新启动。 -
TimeoutSec
:服务启动和关闭时,如果无法顺利执行,会在等待设定的秒数后强制结束服务。 -
KillMode
:终止模式,可以是下面的一种:-
process
:服务终止时只会终止主进程(ExecStart
命令启动的那个) -
control-group
:由服务产生的control-group
进程也会一同关闭 -
none
:不会关闭任何进程
-
-
RestartSec
:重启时,要在关闭后等待多长时间后才启动,默认为100ms。
[Install]
-
WantedBy
:指定当前服务关联到哪些target
下。 -
Also
:当前服务被enable
的时候,这里指定的服务会同样被enable
。 -
Alias
:给服务设定一个别名。
vsftpd实例
在中,我们将vsftpd
的端口改为了555
:
[root@xyz ~]# netstat -utlnp | grep vsftpd
tcp6 0 0 :::555 :::* LISTEN 1251/vsftpd
[root@xyz ~]# ps aux | grep vsftpd
root 1251 0.0 0.0 53292 576 ? Ss 14:39 0:00 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
root 7095 0.0 0.0 112824 980 pts/0 R+ 20:52 0:00 grep --color=auto vsftpd
现在我们尝试通过创建一个新的服务配置的方式运行两个vsftpd
服务,一个使用默认端口,一个使用555
端口:
[root@xyz ~]# cd /etc/vsftpd
[root@xyz vsftpd]# cp vsftpd.conf vsftpd2.conf
[root@xyz vsftpd]# vim vsftpd2.conf
[root@xyz vsftpd]# tail vsftpd2.conf
# sockets. If you want that (perhaps because you want to listen on specific
# addresses) then you must run two copies of vsftpd with two configuration
# files.
# Make sure, that one of the listen options is commented !!
listen_ipv6=YES
pam_service_name=vsftpd
userlist_enable=YES
tcp_wrappers=YES
#listen_port=555
[root@xyz vsftpd]# cd /etc/systemd/system/
[root@xyz system]# cp /usr/lib/systemd/system/vsftpd.service .
[root@xyz system]# cp vsftpd.service vsftpd2.service
[root@xyz system]# vim vsftpd2.service
[root@xyz system]# cat vsftpd2.service | grep -i start
ExecStart=/usr/sbin/vsftpd /etc/vsftpd/vsftpd2.conf
[root@xyz system]# systemctl daemon-reload
[root@xyz system]# systemctl list-unit-files --all | grep vsftpd
vsftpd.service enabled
vsftpd2.service disabled
vsftpd@.service disabled
vsftpd.target disabled
[root@xyz system]# systemctl status vsftpd2
● vsftpd2.service - Vsftpd ftp daemon
Loaded: loaded (/etc/systemd/system/vsftpd2.service; disabled; vendor preset: disabled)
Active: inactive (dead)
[root@xyz system]# systemctl start vsftpd2
[root@xyz system]# systemctl enable vsftpd2
Created symlink from /etc/systemd/system/multi-user.target.wants/vsftpd2.service to /etc/systemd/system/vsftpd2.service.
[root@xyz system]# netstat -tulnp | grep vsftpd
tcp6 0 0 :::555 :::* LISTEN 1251/vsftpd
tcp6 0 0 :::21 :::* LISTEN 7574/vsftpd
这样就可以通过两个使用不同配置文件的服务来启动两个vsfptd
进程,并监听两个不同的端口。
多重的重复设定方式
在Linux启动的时候,默认会提供六个终端:tty1~tty6。事实上六个终端的服务进程是关联在getty.target
之下的,而这六个终端只有编号不同,大部分是相同的,如果要为此创建六个unit配置文件,会相当麻烦,且不便于管理。实际上具体的tty
服务进程是使用getty@.service
这个unit配置创建的:
[root@xyz system]# cat /usr/lib/systemd/system/getty@.service | grep '%I'
Description=Getty on %I
ExecStart=-/sbin/agetty --noclear %I $TERM
UtmpIdentifier=%I
TTYPath=/dev/%I
可以看到,这个配置文件中有一个特殊的变量%I
,这个变量对应的是实际启动服务的时候传入的一个参数,比如如果systemctl start getty@tty7.service
,此时%I
的值就会是tty7
。也就是说%I
会是实际启动服务时@
符号之后的那个字符串。
现在我们看getty@.service
关联到的getty.target
:
[root@xyz system]# systemctl show getty.target | grep -i 'after'
After=getty@tty1.service
这表明了当getty.target
模式启动后,服务getty@tty1.service
就会启动,实际上就是启动了tty1
的进程。
如果我们在虚拟机中依次使用Alt+Ctrl+F1~F6
将所有6个终端开启:
[root@xyz system]# systemctl list-units --all | grep -i -E 'tty[0-9]'
getty@tty1.service loaded inactive dead Getty on tty1
getty@tty2.service loaded active running Getty on tty2
getty@tty3.service loaded active running Getty on tty3
getty@tty4.service loaded active running Getty on tty4
getty@tty5.service loaded active running Getty on tty5
getty@tty6.service loaded active running Getty on tty6
超过F6就没用了,因为默认只给开启6个终端。
现在通过修改配置文件来将可用终端从6个削减到4个:
[root@xyz system]# vim /etc/systemd/logind.conf
[root@xyz system]# cat /etc/systemd/logind.conf | grep -E '(NAutoVTs|ReserveVT)'
#NAutoVTs=6
#ReserveVT=6
NAutoVTs=4
ReserveVT=0
[root@xyz system]# systemctl stop getty@tty5.service getty@tty6.service
[root@xyz system]# systemctl restart systemd-logind.service
现在再在虚拟机中尝试通过Ctrl+Alt+F5
启动终端是没有任何效果的。
但是我们可以通过systemctl
来启动额外的终端:
[root@xyz system]# systemctl start getty@tty8.service
[root@xyz system]# systemctl list-units --all | grep -i -E 'tty[0-9]'
getty@tty1.service loaded inactive dead Getty on tty1
getty@tty2.service loaded active running Getty on tty2
getty@tty3.service loaded active running Getty on tty3
getty@tty4.service loaded active running Getty on tty4
getty@tty8.service loaded active running Getty on tty8
给vsftp新增端口
类似的,vsftpd
同样有一个unitvsftpd@.service
:
[root@xyz system]# cat /usr/lib/systemd/system/vsftpd@.service
[Unit]
Description=Vsftpd ftp daemon
After=network.target
PartOf=vsftpd.target
[Service]
Type=forking
ExecStart=/usr/sbin/vsftpd /etc/vsftpd/%i.conf
[Install]
WantedBy=vsftpd.target
我们可以通过类似的方法很容易地启动多个使用不同配置的vsftpd
服务,且不需要创建多个unit配置:
[root@xyz system]# cd /etc/vsftpd
[root@xyz vsftpd]# ll
总用量 28
-rw-------. 1 root root 125 6月 10 00:15 ftpusers
-rw-------. 1 root root 361 6月 10 00:15 user_list
-rw-------. 1 root root 5133 9月 1 21:18 vsftpd2.conf
-rw-------. 1 root root 5132 8月 30 18:46 vsftpd.conf
-rwxr--r--. 1 root root 338 6月 10 00:15 vsftpd_conf_migrate.sh
[root@xyz vsftpd]# cp vsftpd.conf vsftp3.conf
[root@xyz vsftpd]# vim vsftp3.conf
[root@xyz vsftpd]# cat vsftp3.conf | tail -n 3
userlist_enable=YES
tcp_wrappers=YES
listen_port=2121
[root@xyz vsftpd]# systemctl start vsftpd@vsftp3.service
[root@xyz vsftpd]# netstat -utnlp | grep vsftp
tcp6 0 0 :::2121 :::* LISTEN 8797/vsftpd
tcp6 0 0 :::555 :::* LISTEN 1251/vsftpd
tcp6 0 0 :::21 :::* LISTEN 7574/vsftpd
这里使用
2121
端口不需要修改SELinux,是因为SELinux默认只限制小于1024的端口。
创建自己的服务
下面展示如何创建一个用户自定义服务。
我们要创建的服务主要用途是用来定期备份系统,主要的工作由一个shell
脚本完成:
[icexmoon@xyz ~]$ vim backup_system.sh
[icexmoon@xyz ~]$ cat backup_system.sh
#!/bin/bash
# backup system
backup_dir='/etc /home /root /var/lib /var/spool/{cron,at,mail}'
backup_file="/backups/backup-system-$(date +%Y-%m-%d).tar.gz"
if [ ! -d /backups ]
then
mkdir /backups
fi
if [ ! -f $backup_file ]
then
tar -czvf $backup_file $backup_dir > /backups/backup-system.log 2>&1
fi
exit 0
下面创建一个服务,并通过这个服务来运行这个脚本:
[root@xyz backups]# cd /etc/systemd/system/
[root@xyz system]# vim backup.service
[root@xyz system]# cat backup.service
[Unit]
Description=backup my server
Requires=atd.service
[Service]
Type=simple
ExecStart=/bin/bash -c " echo /home/icexmoon/backup_system.sh | at now"
[Install]
WantedBy=multi-user.target
[root@xyz system]# systemctl start backup.service
[root@xyz system]# systemctl status backup.service
● backup.service - backup my server
Loaded: loaded (/etc/systemd/system/backup.service; disabled; vendor preset: disabled)
Active: inactive (dead)
9月 02 19:41:52 xyz.icexmoon.centos systemd[1]: Started backup my server.
9月 02 19:41:53 xyz.icexmoon.centos bash[2166]: job 6 at Thu Sep 2 19:41:00 2021
[root@xyz system]# ps aux | grep backup
root 2174 0.0 0.1 115404 1464 ? SN 19:41 0:00 /bin/bash /home/icexmoon/backup_system.sh
root 2176 5.4 0.2 125016 2644 ? SN 19:41 0:03 tar -czvf /backups/backup-system-2021-09-02.tar.gz /etc /home /root /var/lib /var/spool/{cron,at,mail}
root 2195 0.0 0.0 112824 980 pts/0 R+ 19:43 0:00 grep --color=auto backup
在启动服务后,服务的状态马上变成了inactive (dead)
,这是因为服务本身只是通过sh
运行了一个at
命令,而具体的脚本的执行是atd
服务进程去启动的,是和服务的主进程无关的,所以服务的主进程在at
命令执行完后就退出了。通过ps
命令可以看到,atd
启动了备份用的脚本,并且脚本触发了tar
命令进行备份。
等一会就能看到备份结果:
[root@xyz system]# ll -h /backups/
总用量 983M
-rw-r--r--. 1 root root 981M 9月 2 19:43 backup-system-2021-09-02.tar.gz
-rw-r--r--. 1 root root 1.9M 9月 2 19:43 backup-system.log
timer unit
之前有提到过服务的unit分类中有一种timer unit,这种配置的用途是可以周期性地启动服务,有点像crond
的用途。
相比crond
,systemd的timer unit具有以下优点:
-
timer unit启动的都是systemd服务,而systemd服务的运行有详细的日志。
-
timer unit本身也是服务,具有systemd服务的特性。
-
timer unit可以和control group(cgroup)结合,用于限制任务的资源。
-
crond
最小单位是分钟,timer unit
的最小单位是毫秒
要使用timer unit
,有以下条件:
-
timer.target
要开启 -
存在
servername.server
服务 -
存在
servername.timer
服务
一般来说周期性运行的服务会创建一个同名的xxx.timer
服务,在这样的情况下它们是自动配对的,如果因为某些原因不同名,则需要在timer unit
中特别指出其对应的server unit
。
timer unit 配置项
timer unit
配置文件以.timer
为后缀名,具体包含以下配置项:
-
OnActiveSec
:timer.target
启动多久之后才启动当前的timer unit
-
OnBootSec
:启动完成之后多久才执行 -
OnStartupSec
:systemd
第一次启动多久后才执行 -
OnunitActiveSec
:timer unit
关联的server unit
最后一次启动后,隔多久再次启动 -
OnUnitInactiveSec
:timer unit
关联的server unit
最后一次停止运行后,隔多久再次启动 -
OnCalendar
:使用明确时间(非间隔时间)来启动server unit
-
Unit
:关联的server unit
(如果同名则无需指定) -
Persistent
:当OnCalendar
被设置时是否要持续运行,如果是yes
,则会类似于crond
或anacron
的效果
使用OnCalendar
OnCalendar
可以使用多种格式的时间单位:
-
us
、usec
:微秒 -
ms
、msec
:毫秒 -
s
、sec
、second
、seconds
-
m
、min
、minute
、minutes
-
h
、hr
、hours
-
d
、day
、days
-
w
、week
、weeks
-
month
、months
-
y
、year
、years
其一般的时间格式为:week YYYY-MM-DD HH:mm:ss
。写法类似于crond
配置文件的写法:
-
每小时零分执行:
*-*-* *:00:00
-
每天零点执行:
*-*-* 00:00:00
-
每周执行第一天执行:
Mon *-*-* 00:00:00
-
每月第一天执行:
*-*-01 00:00:00
案例
这里为之前的backup.service
创建一个对应的timer unit
,并设置为间隔超过两天就执行一次backup.service
:
[root@xyz system]# vim backup.timer
[root@xyz system]# cat backup.timer
[Unit]
Description=backup my server timer
[Timer]
OnBootSec=2hours
OnUnitActiveSec=2days
[Install]
WantedBy=multi-user.target
[root@xyz system]# systemctl daemon-reload
[root@xyz system]# systemctl start backup.timer
[root@xyz system]# systemctl enable backup.timer
[root@xyz system]# systemctl status backup.timer
● backup.timer - backup my server timer
Loaded: loaded (/etc/systemd/system/backup.timer; enabled; vendor preset: disabled)
Active: active (waiting) since 四 2021-09-02 20:24:27 CST; 7s ago
9月 02 20:24:27 xyz.icexmoon.centos systemd[1]: Stopped backup my server timer.
9月 02 20:24:27 xyz.icexmoon.centos systemd[1]: Stopping backup my server timer.
9月 02 20:24:27 xyz.icexmoon.centos systemd[1]: Started backup my server timer.
如果要看timer.target
启动的时间:
[root@xyz system]# systemctl show timers.target | grep -i condition
ConditionResult=yes
ConditionTimestamp=四 2021-09-02 19:08:25 CST
ConditionTimestampMonotonic=20432734
如果要看backup.timer
上次的运行时间:
[root@xyz system]# systemctl show backup.timer | grep -i ActiveExitTimestamp
InactiveExitTimestamp=四 2021-09-02 20:24:27 CST
InactiveExitTimestampMonotonic=4582189621
ActiveExitTimestamp=四 2021-09-02 20:24:27 CST
ActiveExitTimestampMonotonic=4582189532
如果要看backup.timer
下次的运行时间:
[root@xyz system]# systemctl show backup.timer | grep -i Next*
NextElapseUSecRealtime=0
NextElapseUSecMonotonic=2h
固定日期运行
如果要像crond
那样,每个周日零点运行,可以:
[root@xyz system]# vim backup_weekend.timer
[root@xyz system]# cat backup_weekend.timer
[Unit]
Description=backup my server timer2
[Timer]
OnCalendar=Sun *-*-* 00:00:00
Persistent=true
Unit=backup.service
[Install]
WantedBy=multi-user.target
[root@xyz system]# systemctl daemon-reload
[root@xyz system]# systemctl start backup_weekend.timer
[root@xyz system]# systemctl enable backup_weekend.timer
Created symlink from /etc/systemd/system/multi-user.target.wants/backup_weekend.timer to /etc/systemd/system/backup_weekend.timer.
[root@xyz system]# systemctl status backup_weekend.timer
● backup_weekend.timer - backup my server timer2
Loaded: loaded (/etc/systemd/system/backup_weekend.timer; enabled; vendor preset: disabled)
Active: active (waiting) since 四 2021-09-02 20:46:02 CST; 19s ago
9月 02 20:46:02 xyz.icexmoon.centos systemd[1]: Started backup my server timer2.
[root@xyz system]# systemctl show backup_weekend.timer | grep -i next
NextElapseUSecRealtime=51y 8month 3d 10h
NextElapseUSecMonotonic=0
最后那个之所以下次运行时间为51y 8month 3d
是因为这里显示的是时间戳时间,也就是相对于1970-01-01 00:00:00
的时间。
最后不要忘了将这几个示例创建的定期服务都关闭掉,否则会产生很多备份文件占用空间:
[root@xyz system]# systemctl list-unit-files --all | grep backup
backup.service disabled
backup.timer enabled
backup_weekend.timer enabled
[root@xyz system]# systemctl disable backup.service backup.timer backup_weekend.timer
Removed symlink /etc/systemd/system/multi-user.target.wants/backup.timer.
Removed symlink /etc/systemd/system/multi-user.target.wants/backup_weekend.timer.
[root@xyz system]# systemctl stop backup.service backup.timer backup_weekend.timer
关于系统服务管理的内容介绍完毕,谢谢阅读。
文章评论