红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 专栏
  3. Linux之旅
  4. 正文

Linux 之旅 17:系统服务(daemons)

2021年9月2日 1704点热度 1人点赞 0条评论

image-20210902210042202

图源:pexels

daemon与service

daemon可以直译为守护进程,在Linux中特指服务进程。

service指服务,通常指提供某种功能的常驻后台的应用,可能会由多个服务进程组成。

在Linux中,这两者一般可以简单地等同,都可以指代服务进程,不过一般会使用daemon这个称呼。

关于daemon一词的来源和故事可以阅读计算机daemon和通过死循环构造的进程的区别是什么?

systemd

早期的Linux发行版使用System V管理服务进程,但从CentOS 7.x开始,已经普遍使用systemd管理服务。

如果想了解早期的System V的相关内容,可以阅读早期 System V 的 init 管理行為中 daemon 的主要分類 (Optional)。

相对于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

此时如果查看虚拟机,可以发现已经从图形界面变成了命令行模式:

image-20210901150340237

不用担心,要切换回来也很简单:

[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是一个局域网内路由的功能(详细说明见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实例

在Linux 之旅 16:SELinux 初探中,我们将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

关于系统服务管理的内容介绍完毕,谢谢阅读。

参考资料

  • linux服务管理程序systemd

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: daemon Linux 服务
最后更新:2021年9月2日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇
下一篇 >

文章评论

取消回复

*

code

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号