systemctl针对service类型的配置文件

这篇具有很好参考价值的文章主要介绍了systemctl针对service类型的配置文件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

systemctl针对service类型的配置文件

systemctl配置文件相关目录简介

现在我们知道服务的管理是通过systemd来完成,而systemd的配置文件大部分放置于/usr/lib/systemd/system/ 目录中,但是Red Hat官方文件指出,该目录的文件主要是原本软件所提供的设置,建议不要修改。而要修改的位置应该放置于/etc/systemd/system目录中。举例来说你想要额外修改vsftpd.service的话,官方建议你放在哪些地方呢?

  • /usr/lib/systemd/system/vsftpd.service:官方发布的默认配置文件

  • /etc/systemd/system/vsftpd.serivce.d/custom.conf:在/etc/systemd/system 下面建立于配置文件相同的目录,但是要加上.d的扩展名,然后在该目录下建立配置文件即可。另外,配置文件的扩展名最好使用.conf。在这目录下的文件会【累加其他设置】到 /usr/lib/systemd/system/vsftpd.service中。

  • /etc/systemd/system/vsftpd.service.wants/*:此目录内的文件为链接文件,设置依赖服务的链接,意思是启动vsftpd.service之后,最好再加上该目录下面建议的服务。

  • /etc/systemd/system/vsftpd.service.requires/*:此目录内的文件为链接文件,设置依赖服务的链接。意思就是在启动vsftpd.service 之前,需要事先启动哪些服务的意思。

基本上,在配置文件里你都可以自由设置依赖服务的检查,并且设置加入到哪些target里面。但是如果是已经存在的配置文件,或是官方提供的配置文件,Red Hat建议你不要修改原设置,而是到上面提到的几个目录去进行额外的自定义设置比较好,如果你硬要修改原始的/usr/lib/systemd/system 下面的配置文件,那也是没问题的。

systemctl配置文件的设置项目简介

了解完目录在了解一下文件本身的内容,让我们来看看sshd.service的内容。

[root@localhost ~]# cat /usr/lib/systemd/system/sshd.service 
[Unit]      # 这个项目与此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]   # 这个项目说明此unit要挂载哪个到target下面。
WantedBy=multi-user.target

[Service]、[Socket]、[Timer]、[Mount]、[Path]:不同的unit类型就得要使用相对应的设置项目。我们使用sshd.service来当模板,所以这边就使用[service]来设置。

分析上面的配置文件,大概可以将整个设置分为3个部分

  1. [Unit] 部分包含了服务的元数据,Description 字段说明了服务的描述信息,即 OpenSSH 服务器守护进程。Documentation 字段指定了服务的文档信息,即 sshd 服务的 man 页面。After 字段指定了服务需要在 network.targetsshd-keygen.service 启动之后启动,Wants 字段指定了服务需要依赖于 sshd-keygen.service 服务。
  2. [Service] 部分定义了服务的具体行为,Type 字段指定了服务的类型为 notify,表示当服务准备就绪时会发送一个通知给 systemd。EnvironmentFile 字段指定了服务需要读取的环境变量文件,ExecStart 字段指定了服务的启动命令和选项,ExecReload 字段指定了服务的重载命令。KillMode 字段指定了服务的停止策略,Restart 字段指定了服务的重启策略为在失败时重启,RestartSec 字段指定了服务重启的时间间隔为 42 秒。
  3. [Install] 部分定义了服务的安装方式,WantedBy 字段指定了服务需要在 multi-user.target 启动之后启动。

此外每个部分里面还有很多的设置,如下所示

unit部分

设置参数 参数意义说明
Description 是当我们使用【systemctl list-units】时,会输出给管理员看的建议说明信息。
Documentation 这个项目在提供给管理员能够进行进一步的文件查询的功能。提供的文件可以是如下内容:
Documentation=http://www…
Documentation=man:sshd(8)
Documentation=file:/etc/ssh/sshd_config
After 说明此unit是在哪个daemon启动之后才启动的意思。基本上仅是说明服务启动顺序而已,并没有强制要求里面的服务一定启动后,此unit才能启动。
Before 与After的意义相反,是在什么服务启动前最好启动这个服务的意思。仅是规范服务启动顺序,并非强制要求的意思
Requires 明确的定义unit需要在哪个daemon启动后才能够启动,就是设置依赖服务。如果在此项设置的前导服务没有启动,那么此unit就不会被启动
Wants 与Requires刚好相反,规范的是这个unit之后最好还要启动什么服务比较好的意思。如果Wants后面接的服务没有启动,其实不会影响到这个unit本身
Conflicts 代表冲突的服务。就是这个项目后面接的服务如果有启动,那么我们这个unit本身就不能启动。如果我们unit有启动,则此项目后面的服务就不能启动。就是冲突性的检查

Service部分

设置参数 参数意义说明
Type 该字段指定了服务的类型,一般来说有如下几种类型:
1. simple:最常用的类型,用于启动一个进程,systemd会等待这个进程运行完毕后再启动下一个服务。
2. forking:用于启动一个进程,但是systemd会等待这个进程fork出一个子进程,然后父进程退出,而子进程则继续运行。常用于一些需要后台运行的服务,比如nginx。
3. oneshot:用于只需要执行一次的服务,systemd会等待这个服务执行完毕后就退出。
4. dbus:用于启动一个dbus服务。
5. notify:用于启动一个进程,但是这个进程会在运行完毕后向systemd发送一个通知,告诉它服务已经启动完毕了。
6. idle:用于启动一个进程,但是会等待一段时间,直到系统处于空闲状态时再启动服务。
EnvironmentFile 指定启动脚本需要读取的环境配置文件。
ExecStart 字段指定了服务的启动命令和选项
ExecStop 与systemctl stop 的执行有关,关闭此服务时所运行的命令
ExecReload 与systemctl reload 有关的命令操作
Restart 指定服务的重启策略
RemainAfterExit 当设置RemainAfterExit=1时,则当这个daemon所属的所有进程都终止之后,此服务会再尝试启动,如果设置为0,则服务再退出后视为不再启动。
TimeoutSec 若这个服务在启动或关闭时,因为某些缘故导致无法顺利【正常启动或正常结束】的情况下,则我们要等多久才进入【强制结束】的状态。
KillMode 字段指定了服务的停止策略
RestartSec 字段指定了服务重启的时间

Install部分

设置参数 参数意义说明
WantedBy 指定服务需要在哪个target启动之后启动,而且这个服务必须是启动的
Also 指定与此服务一起启动的服务。
Alias 运行一个链接的别名的意思

接下来根据上面这些数据来进行一些简易的操作

两个vsftpd运行的实例

因为某些原因,你可能会使用到两个端口,分别是正常的21以及特殊的555.这两个port都启用的情况下,你可能使用到两个配置文件以及两个启动脚本设置。假设是这样:

  • 默认的21端口:使用/etc/vsftpd/vsftpd.conf 配置文件,以及/usr/lib/systemd/system/ vsftpd.service设置脚本
  • 特殊的2121端口:使用/etc/vsftpd/vsftpd2.conf 配置文件,以及 /etc/systemd/system/ vsftpd2.service 设置脚本。

我们可以这样做

  1. 先建立好需要的配置文件
[root@localhost ~]# cd /etc/vsftpd/
[root@localhost vsftpd]# cp vsftpd.conf vsftpd2.conf
[root@localhost vsftpd]# vim vsftpd.conf 
#listen_port=2121
[root@localhost vsftpd]# vim vsftpd2.conf 
listen_port=2121
[root@localhost vsftpd]# diff vsftpd.conf vsftpd2.conf 
129c129
< #listen_port=2121
---
> listen_port=2121
# 注意这两个配置文件的差别,只有这一行不同而已
  1. 开始处理启动脚本设置
[root@localhost vsftpd]# cd  /etc/systemd/system/
[root@localhost system]# cp /usr/lib/systemd/system/vsftpd.service vsftpd2.service
[root@localhost system]# vim vsftpd2.service 
[Unit]
Description=Vsftpd ftp daemon
After=network.target

[Service]
Type=forking
ExecStart=/usr/sbin/vsftpd /etc/vsftpd/vsftpd2.conf

[Install]
WantedBy=multi-user.target
  1. 重新加载systemd的脚本配置文件内容
[root@localhost system]# systemctl daemon-reload
[root@localhost system]# systemctl list-unit-files --all |grep vsftpd
vsftpd.service                                enabled 
vsftpd2.service                               disabled
vsftpd@.service                               disabled
vsftpd.target                                 disabled
[root@localhost system]# systemctl status vsftpd2.service
● vsftpd2.service - Vsftpd ftp daemon
   Loaded: loaded (/etc/systemd/system/vsftpd2.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
[root@localhost system]# systemctl restart vsftpd.service vsftpd2.service
[root@localhost system]# systemctl enable vsftpd.service vsftpd2.service 
Created symlink from /etc/systemd/system/multi-user.target.wants/vsftpd2.service to /etc/systemd/system/vsftpd2.service.

systemctl配置文件service,Linux基础学习,服务器,linux,网络

systemctl配置文件service,Linux基础学习,服务器,linux,网络

很简单地将你的systemd所管理的vsftpd做了另一个相同的服务。如果你有需求按照上面的方法做就行了。

多重的重复设置方式:以getty为例

我们的Centos 7启动完成后,不是说有6个终端可以使用吗?就是tty1~tty6,这个东西是有agetty命令完成的。那么这个终端的功能又是哪个项目所提供的呢?其实,这个东西涉及很多层面,主要管理的是getty.target 这个target unit,不过,实际产生tty1~tty6的则是由getty@.service所提供。

先来看看/usr/lib/systemd/system/getty@.service的内容

[root@localhost ~]# cat /usr/lib/systemd/system/getty@.service
[Unit]
Description=Getty on %I
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
After=rc-local.service
Before=getty.target
IgnoreOnIsolate=yes
ConditionPathExists=/dev/tty0
[Service]
ExecStart=-/sbin/agetty --noclear %I $TERM
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes
[Install]
WantedBy=getty.target

当然比较重要的就是ExecStart项目。那么我们去man agetty时,发现到它的语法应该是【agetty --noclear tty1】之类的字样,因此,我们如果要启动6个tty的时候,基本上应该有6个启动配置文件。就是可能会用到getty1.service、getty2.service…getty6.service才对。但是这样管理很麻烦,所以,才会出现 @ 的项目。这个@是怎么回事呢?先来看看getty@.service 的上游

[root@localhost ~]# systemctl show getty.target
Names=getty.target
Wants=getty@tty1.service
WantedBy=multi-user.target
Conflicts=shutdown.target
Before=multi-user.target
After=getty@tty2.service getty@tty1.service getty@tty3.service getty@tty4.service getty@tty6.service getty@tty5.service
.....
......
....

你会发现,怎么多出了6个怪异的service呢?我们拿getty@tty1.service 说明一下。当我们执行完getty.target之后,它会持续要求 getty@tty1.service 等 6个服务继续启动。那我们的systemd就会这么做

  • 先看 /usr/lib/systemd/system/、/etc/systemd/system/ 有没有getty@tty1.service 的设置,若有就执行,若没有则执行下一步
  • 找getty@.service的设置,若有则将@后面的数据带入成%I 的变量,进入getty@.service执行。

也就是说,其实getty@tty1.service实际上是不存在的。主要是通过getty@.service来执行。也就是说,getty@.service的目的是为了要简化多个执行的启动设置,它的命名方式是这样的

原始文件:执行服务名称@.service
执行文件:执行服务名称@范例名称.service

因此当有范例名称带入时,则会有一个新的服务名称产生。你再回头看看getty@.service的启动脚本

ExecStart=-/sbin/agetty --noclear %I $TERM

上表中那个 %I 指定就是【范例名称】。根据getty.target 的信息输出来看,getty@tty1.service 的 %I 就是tty1,因此执行脚本就会变成【/sbin/agetty --noclear tty1】。所以我们才有办法以一个配置文件来启动多个tty1给用户登录。

将tty数量由6个降低为4个

现在你应该会感到困扰的是,那么【6个tty是谁规定的】?为什么不是5个或是7个?这是由systemd的登录配置文件 /etc/systemd/logind.conf 里面规范的。加入你想要tty数量降低到剩下4个的话,可以这样实验

  1. 修改默认的logind.conf内容,将原本6个终端改为4个
[root@localhost ~]# vim /etc/systemd/logind.conf
[Login]
NAutoVTs=4
ReserveVT=0
....
# 原本是6个而且还注释,请取消注释,然后改成4
  1. 关闭不小心启动的tty5、tty6并重新启动getty.target
[root@localhost ~]# systemctl stop getty@tty5.service
[root@localhost ~]# systemctl stop getty@tty6.service
[root@localhost ~]# systemctl restart systemd-logind.service

现在你再到桌面环境下,按下[ctrl]+[Alt]+[F1]~[F6]就会发现,只剩下4个可用tty,后面的tty5、tty6已经被废弃,不再被启动。那么我暂时需要启动tty8时,又该如何处理?可以这样做

[root@localhost ~]# systemctl start getty@tty8.service

无须额外建立其他的启动服务配置文件

暂时新增vsftpd到1212端口

不知道你有没有发现,其实再/usr/lib/systemd/system/ 下面还有个特别的 vsftpd@.service 。来看看它的内容

[root@localhost ~]# cat /usr/lib/systemd/system/vsftpd@
@::1                      @localhost4               @localhost6               @localhost.localdomain    
@localhost                @localhost4.localdomain4  @localhost6.localdomain6  
[root@localhost ~]# 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

根据前面getty@.service的说明,我们知道在启动的脚本设置当中,%i或%I 就是代表@后面接的范例文件名的意思。那么能不能建立vsftpd3.conf文件,如何通过该文件来启动新的服务?下面试试看

[root@localhost ~]# cd /etc/vsftpd/
[root@localhost vsftpd]# cp vsftpd.conf vsftpd3.conf
[root@localhost vsftpd]# vim vsftpd3.conf
listen_port=1212

systemctl配置文件service,Linux基础学习,服务器,linux,网络

systemctl配置文件service,Linux基础学习,服务器,linux,网络

因为我们启用了vsftpd@vsftpd3.service,代表要实验的配置文件在 /etc/vsftpd/vsftpd3.conf 的意思,所以可以直接通过vsftpd@.service 而无须重新设置启动脚本。这样设置会比前面一个小节中的方法要简单,不过你要注意到 @ 这个东西。

自己做个服务

我们自己做个服务。假设我要制作一个可以备份自己系统的服务,这个脚本放在/backups下面,内容如下

[root@localhost ~]# vim /backups/backup.sh
#!/bin/bash
source="/home /root /var/lib /var/spool/{cron,at,mail}"
target="/backups/backup-system-$(date +%Y-%m-%d).tar.gz"
[ ! -d /backups ] && mkdir /backups
tar -zcvf ${target} ${source} &> /backups/backup.log

脚本解释:

这是一个bash脚本,用于备份系统的重要文件和目录。下面是每个部分的解释:

  1. source=“/home /root /var/lib /var/spool/{cron,at,mail}”:这个变量定义了需要备份的目录,包括/home、/root、/var/lib、/var/spool/cron、/var/spool/at和/var/spool/mail。
  2. target=“/backups/backup-system-$(date +%Y-%m-%d).tar.gz”:这个变量定义了备份文件的路径和名称,其中$(date +%Y-%m-%d)表示当前日期,tar.gz表示压缩格式。
  3. [ ! -d /backups ] && mkdir /backups:如果/backups目录不存在,则创建这个目录。
  4. tar -zcvf ${target} ${source} &> /backups/backup.log:使用tar命令将source变量定义的目录打包成一个压缩文件,保存到target变量定义的位置。-z表示使用gzip进行压缩,-c表示创建新的存档文件,-v表示显示打包的详细过程,-f表示指定存档文件的名称。使用&>将tar命令的输出和错误信息都重定向到/backups/backup.log文件中,以便记录备份过程中出现的任何问题。
[root@localhost ~]# chmod a+x /backups/backup.sh 
[root@localhost ~]# ll /backups/backup.sh 
-rwxr-xr-x. 1 root root 215 514 23:29 /backups/backup.sh
# 记得要赋予它可执行权限才行

那么接下来如何设计一个名为backup.service的启动脚本设置?可以如下这样做

[root@localhost ~]# vim /etc/systemd/system/backup.service
[Unit]
Description=backup my server
Requires=atd.service
[Service]
Type=simple
ExecStart=/bin/bash -c "echo /backups/backup.sh | at now"
[Install]
WanteBy=multi-user.target
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl start backup.service
[root@localhost ~]# systemctl status backup.service
● backup.service - backup my server
   Loaded: loaded (/etc/systemd/system/backup.service; static; vendor preset: disabled)
   Active: inactive (dead)

514 23:11:38 localhost.localdomain systemd[1]: [/etc/systemd/system/backup.service:8] Unknown lvalu...ll'
5月 14 23:11:38 localhost.localdomain systemd[1]: Started backup my server.
5月 14 23:11:38 localhost.localdomain bash[3022]: job 1 at Sun May 14 23:11:00 2023
5月 14 23:11:45 localhost.localdomain systemd[1]: [/etc/systemd/system/backup.service:8] Unknown lvalu...ll'
Hint: Some lines were ellipsized, use -l to show in full.
# 为什么Active是inactive?这是因为我们的服务仅是一个简单的脚本
# 因此执行完毕就完毕了,不会继续常驻在内存中。
[root@localhost backups]# cd /backups/
[root@localhost backups]# ls
backup.log  backup.sh  backup-system-2023-05-14.tar.gz

配置文件解释:

  1. [Unit]:这个部分是设置服务的描述信息和依赖关系,Description是服务的描述信息,Requires=atd.service 表示这个服务依赖于 atd.service 服务,也就是说,只有在 atd.service 运行的情况下,这个服务才能运行。
  2. [Service]:这个部分是设置服务的运行参数,Type=simple 表示这个服务是最常用的类型,即启动一个进程,systemd会等待这个进程运行完毕后再启动下一个服务。ExecStart=/bin/bash -c “echo /backups/backup.sh | at now” 表示启动这个服务时,会执行这个命令,即在当前时间调用 at 命令,将备份脚本 /backups/backup.sh 添加到任务队列中,并在任务队列中立即执行。
  3. [Install]:这个部分是设置服务的安装信息,WantedBy=multi-user.target 表示将这个服务添加到 multi-user.target 的依赖关系中,也就是说,系统启动时会自动启动这个服务。

完成上述的操作之后,以后你都可以直接实验【systemctl start backup.service 】进行系统的备份了。非常方便!文章来源地址https://www.toymoban.com/news/detail-752000.html

到了这里,关于systemctl针对service类型的配置文件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 书写systemctl配置文件管理tomcat

    cat /usr/lib/systemd/system/tomcat.service [Unit] Description =Tomcat java web container After =network.target [Service] Type =forking #EnvironmentFile=/etc/sysconfig/tomcat ExecStart =/app/tools/tomcat/bin/startup.sh ExecStop =/app/tools/tomcat/bin/shutdown.sh [Install] WantedBy =multi-user.target #重新加载systemctl 配置 systemctl daemon-reload

    2024年01月21日
    浏览(44)
  • systemctl status mysqld.service或者systemctl start mysqld 启动失败的解决办法

    报错过程: mysql需要启动,执行一下命令启动mysql: 结果出现如下提示 在这里给大家一个提示,因为每个人的报错原因不同,我们需要去查看mysql的日志 如果mysql日志太多,可以通过dG将mysql的日志全部删除,之后再执行启动musql的命令 找到日志中出现error的地方,根据具体问

    2024年02月08日
    浏览(68)
  • 常见类型的yaml文件如何编写?--kind: Service

    在 Kubernetes 中, Service  是一种抽象的方式,用于定义一组 Pod 的访问方式和网络服务。Service 提供了一个稳定的网络端点(Endpoint),使得其他服务或外部用户可以通过 Service 来访问被管理的 Pod。 负载均衡 :Service 可以将流量均匀地分发到一组后端 Pod。通过使用 Service,您无

    2024年01月22日
    浏览(48)
  • systemctl enable docker.service报错“Failed to execute operation: Bad message“

    将docker加入到开机自启,报错: 解决:  重新粘贴复制:

    2024年02月03日
    浏览(40)
  • 计算节点systemctl status openstack-nova-compute.service起不来的解决方案

    报错 [root@compute ~]# systemctl start libvirtd.service openstack-nova-compute.service Job for openstack-nova-compute.service failed because the control process exited with error code. See \\\"systemctl status openstack-nova-compute.service\\\" and \\\"journalctl -xe\\\" for details. ● openstack-nova-compute.service - OpenStack Nova Compute Server    Loaded: loade

    2024年02月03日
    浏览(43)
  • service和systemctl的区别/修改PATH的方法/一条命令查看IP地址和网关以及DNS服务器

    一.service和systemctl的区别 Linux服务管理的两种方式:service 和 systemctl service命令其实是去/etc/init.d目录下,去执行相关程序,init.d目录包含许多系统各种服务的启动和停止脚本。当Linux启动时,会寻找这些目录中的服务脚本,并根据脚本的run level确定不同的启动级别。 这种方法

    2024年02月05日
    浏览(67)
  • systemctl status network.service命令,Failed to start LSB: Bring up/down networking.完美解决

    1.场景描述,我在电脑里装好的虚拟机,今天突然就网络无法打开,并出现如下报错: [root@ansible-control ~]# systemctl start network Job for network.service failed because the control process exited with error code. See \\\"systemctl status network.service\\\" and \\\"journalctl -xe\\\" for details. [root@ansible-control ~]# systemctl stat

    2024年02月14日
    浏览(43)
  • linux进阶55——service文件

    创建ping@.service文件,内容可以如下: 将.service文件拷贝至/usr/lib/systemd/system/下 systemctl是一个systemd工具,负责控制systemd系统和管理系统服务。 主要包括服务描述、启动顺序和依赖关系。 Description:当前服务的简单描述; Documentation:文档位置; After:在什么服务之后启动;

    2024年02月16日
    浏览(33)
  • linux 之.service文件简介

    什么是.service文件? Linux中.service文件是某项服务对应的配置文件,可用于systemd管理和控制的服务的设置。 .service 文件通常包含3个模块,即[Unit]控制单元,表示启动顺序和依赖关系;[Service]服务,表示服务的定义;[Install]安装,表示如何安装配置文件。 文件存放位置 .servi

    2024年02月05日
    浏览(26)
  • 针对于OB_GINS的CMakeList文件的深入学习

    project(项目名称 VERSION major.minor.patch.tweak )CMake会将对应的值分别赋值给以下变量: configure_file指令 configure_file 指令通过读取输入文件中的内容,将 CMakeLists.txt 文件中的变量转变为 C/C++ 中可识别的宏定义,然后存入另一个文件中。 configure_file,复制一份输入文件到输出文件,替

    2024年04月13日
    浏览(22)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包