1、用法:systemctl [OPT] COMMAND [NAME]…
启动服务:systemctl start name.service
停止服务:systemctl stop name.service
重启服务:systemctl restart name.service
服务状态:systemctl status name.service
服务重载:systemctl daemon-reload
条件式重启,已启动才重启,否则不作操作:systemctl tryrestart name.service
重载或重启服务,先加载再启动:systemctl reloadorrestart name.service
重载或条件式重启:systemctl reloadortryrestart name.service
禁止自动和手动启动:systemctl mask name.service
取消禁止:systemctl unmask name.service
查看某服务当前激活与否的状态:systemctl isactive name.service
查看所有已经激活的服务:systemctl listunits t service
查看所有服务:systemctl listunits t service a
设定某服务开机自启动:systemctl enable name.service
设定某服务开机禁止启动:systemctl disable name.service
查看所有服务的开机自启动状态:systemctl listunitfiles –t service
列出该服务在哪些运行级别下启用和禁止:ls /etc/systemd/system/*.wants/sshd.service
查看服务是否开机启动:systemctl isenabled name.service
查看服务的依赖关系:systemctl listdependencies name.service
杀掉进程:systemctl kill (进程名)
2、服务状态:
loaded:unit配置文件已处理
active(running):一次或多次持续处理的运行
active(exited):成功完成一次性配置
active(waiting):运行中,等待一个事件
inactive:不运行
enable:开机启动
disable:开机不启动
static:开机不启动,但可以被另一个启用的服务激活。
一、QT实现后台服务
QT使用开源项目QtService实现后台服务,支持windows和linux跨平台使用,下载地址:
https://github.com/qtproject/qt-solutions/tree/master/qtservice
QT新建控制台程序"Qt Console Application",把qtservice文件夹拷贝到QT工程目录,在pro文件添加:
include(qtservice/src/qtservice.pri)
1
添加新文件:ubuntuservice.h和ubuntuservice.cpp,服务功能主要由重写的几个虚函数完成:start、stop、pause、resume,在启动、停止、暂停、重启服务时会自动调用这些虚函数。
下面的demo用QProcess启动停止外部程序,功能在外部程序实现(test_systemd),这里外部程序的功能是启动一个TCP客户端,连接到服务器,当收到服务器的消息时自动回复一条confirm消息。
修改main.cpp
#include "ubuntuservice.h"
int main(int argc, char *argv[])
{
ubuntuService service(argc, argv);
return service.exec();
}
1
2
3
4
5
6
ubuntuservice.h
#ifndef UBUNTUSERVICE_H
#define UBUNTUSERVICE_H
#include <QCoreApplication>
#include <QProcess>
#include "qtservice.h"
class ubuntuService : public QObject,public QtService<QCoreApplication>
{
Q_OBJECT
public:
ubuntuService(int argc, char **argv);
~ubuntuService();
private:
QProcess *pExternal;
protected:
void start()override;
void stop()override;
void pause()override;
void resume()override;
public slots:
void slotpExternalFinished(int exitCode, QProcess::ExitStatus exitStatus);
};
#endif // UBUNTUSERVICE_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ubuntuservice.cpp
#include "ubuntuservice.h"
#include <QDebug>
ubuntuService::ubuntuService(int argc, char **argv)
: QtService<QCoreApplication>(argc, argv, "QtServiceDemo")
{
setServiceDescription("QtServiceDemo");
setServiceFlags(QtServiceBase::CanBeSuspended);
pExternal = new QProcess();
connect(pExternal, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotpExternalFinished(int, QProcess::ExitStatus)));
}
ubuntuService::~ubuntuService()
{
delete pExternal;
}
///开始服务
void ubuntuService::start()
{
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"ubuntuService ";
//启动了一个新进程后,父进程继续自己的工作
//如果使用start,父进程会suspend,只有子进程结束,父进程才会继续
pExternal->startDetached("/home/chw/release/test_systemd");
}
///停止服务
void ubuntuService::stop()
{
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"ubuntuService ";
pExternal->close();
}
///暂停服务
void ubuntuService::pause()
{
}
///重启服务
void ubuntuService::resume()
{
}
//外部程序关闭时触发该槽函数
void ubuntuService::slotpExternalFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
QString exitStr = QString("软件退出,exitCode=%1,ExitStatus=%2.").arg(QString::number(exitCode)).arg(exitStatus);
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<" "<<exitStr;
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<" "<<pExternal->errorString();
stop();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
二、systemd使用说明
Systemd 是 Linux 的系统和服务的管理器,systemd即为system daemon,是linux下的一种init软件,是一种系统守护进程,其提供更优秀的框架以表示系统服务间的依赖关系,并依此实现系统初始化时服务的并行启动,同时达到降低Shell的系统开销的效果。
Systemd 管理 unit,unit代表系统资源和服务,unit有很多种类型,我们使用其中的service:系统上的一项服务,包括启动、重新启动和停止服务。
1、systemctl常用命令
systemd对应的进程管理命令是systemctl。
systemctl start my-demo.service#启动
systemctl stop my-demo.service#停止
systemctl restart my-demo.service#重启
systemctl status my-demo.service#查看服务状态,包含日志
systemctl list-unit-files#列出所有可用单元
systemctl list-units#列出所有运行中单元
systemctl --failed#列出所有失败单元
systemctl kill network.service #使用systemctl命令杀死服务
systemctl daemon-reload#修改service文件后,重新加载unit配置
systemctl enable my-demo.service #设置开机启动
systemctl disable my-demo.service #取消开机启动
systemctl is-enabled my-demo.service #查看是否开机启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2、unit配置文件
unit 配置文件主要分为三部分:[Unit]、[Service] 和 [Install]
[Unit]部分详解
此部分中使用的参数不仅限于 service 类型的 unit,对其它类型 unit 也是通用的,有关这些参数及其说明的完整列表,可运行命令 man systemd.unit 或访问 systemd.unit 中文手册
注意:在 [unit] 块中的每个参数后都可以指定一个以空格分隔的列表
Description:当前 unit 的描述
Documentation:文档地址,仅接受类型为:http://、https://、file:、info:、man: 的URI
Requires:表示本 unit 与其它 unit 之间存在强依赖关系,如果本 unit 被激活,此处列出的 unit 也会被激活,如果其中一个依赖的 unit 无法激活,systemd 都不会启动本 unit
Wants:与 Requires 类似,区别在于如果依赖的 unit 启动失败,不影响本 unit 的继续运行
After:表示本 unit 应该在某服务之后启动,选项可参考 systemd.special 中文手册
Before:表示本 unit 应该在某服务之前启动,After和Before 字段只涉及启动顺序,不涉及依赖关系
BindsTo:与 Requires 类似,当指定的 unit 停止时,也会导致本 unit 停止
PartOf:与 Requires 类似,当指定的 unit 停止或重启时,也会导致本 unit 停止或重启
Conflicts:如果指定的 unit 正在运行,将导致本 unit 无法运行
OnFailure:当本 unit 进入故障状态时,激活指定的 unit
[Service]部分详解
service专有参数
只有 service 类型的 unit 才有这些参数,参数完整列表请访问 systemd.service 中文手册 或 systemd.service
simple(默认值):服务为主进程启动,systemd 认为该服务将立即启动,服务进程不会 fork ,如果该服务要启动其他服务,则不要使用此类型启动,除非该服务是 socket 激活型。
forking:服务将以fork分叉的方式启动,此时父进程将会退出,子进程将成为主进程。systemd 认为当该服务进程 fork,且父进程退出后服务启动成功。对于常规的守护进程(daemon)除非你确定此启动方式无法满足需求,否则使用此类型启动即可。使用此启动类型应同时指定 PIDFile= 以便 systemd 能够跟踪服务的主进程
oneshot:类似于simple,但只执行一次,Systemd会等它执行完,才启动其它服务。这一选项适用于只执行一项任务,随后立即退出的服务。可能需要同时设置 RemainAfterExit=yes 使得 systemd 在服务进程退出之后仍然认为服务处于激活状态
dbus:类似于simple,但会等待D-Bus信号后启动。当指定的 BusName 出现在DBus系统总线上时,systemd 认为服务就绪。
notify:类似于simple,启动完毕后会通知 Systemd ,然后继续往下执行
idle:类似于simple,但是要等到其它所有任务都执行完,才会启动该服务
[Install]部分详解
[install] 定义了 unit 的安装信息,此部分配置仅在 systemctl enable 或 systemctl disable 时使用,在 unit 运行时不解释此部分,相当于是配置如何开机启动
此部分中使用的参数不仅限于service类型的unit,对其它类型的 unit也是通用的,有关这些参数及其说明的完整列表,可运行命令 man systemd.unit 或访问 systemd.unit 中文手册
Alias:当前 unit 可用于启动的别名,此处列出的名称必须与服务文件名具有相同的后缀(即类型),在执行 systemctl enable 时将创建从这些名称到 unit 文件名的符号链接
RequiredBy:表示该服务所在的Target,它的值是一个或多个Target,当 systemctl enable 时 unit 符号链接会放入 /etc/systemd/system 目录下面以 Target名 + .required 后缀构成的子目录中
WantedBy:表示该服务所在的Target,它的值是一个或多个Target,当前 systemctl enable 时 unit符号链接会放入 /etc/systemd/system 目录下面以 Target名 + .wants 后缀构成的子目录中
Also:当 systemctl enable 或 systemctl disable 时会同时 enable 和 disable 的其它 unit 列表
三、systemd管理Qt写的后台程序
新建一个服务unit文件
cd /usr/lib/systemd/system/
sudo vim test_systemd.service
1
2
编写unit文件
[Unit]
Description=test_systemd Service
[Service]
Type=forking
ExecStart=/bin/bash /home/chw/release/test_qtservice.sh
ExecStop=/bin/bash /home/chw/release/test_qtservice_quit.sh
StandardOutput=syslog
StandardError=inherit
[Install]
WantedBy=multi-user.target
1
2
3
4
5
6
7
8
9
10
将服务注册到系统
systemctl enable my-demo.service#将服务注册到系统
Created symlink /etc/systemd/system/multi-user.target.wants/test_systemd.service → /lib/systemd/system/test_systemd.service.
cd /etc/systemd/system/multi-user.target.wants/#上一步注册是在这个文件夹创建软链接
1
2
3
ExecStart和ExecStop是两个脚本文件,分别对应启动服务和停止服务
test_qtservice.sh
#!/bin/sh
workdir=$(cd $(dirname $0); pwd)
export LD_LIBRARY_PATH=$workdir
cd $workdir
./test_qtservice -s
1
2
3
4
5
test_qtservice_quit.sh
#!/bin/sh
workdir=$(cd $(dirname $0); pwd)
export LD_LIBRARY_PATH=$workdir
cd $workdir
./test_qtservice -t
1
2
3
4
5
6
test_qtservice是使用QtService写的服务程序,后面跟-s代表启动服务,-t代表停止服务,也就是会调用QtService的start和stop虚函数。
其他含义:
-[i|u|e|t|p|r|c|v|h]\n"
"\t-i(nstall) [account] [password]\t: Install the service, optionally using given account and password\n"
"\t-u(ninstall)\t: Uninstall the service.\n"
"\t-e(xec)\t\t: Run as a regular application. Useful for debugging.\n"
"\t-t(erminate)\t: Stop the service.\n"
"\t-p(ause)\t: Pause the service.\n"
"\t-r(esume)\t: Resume a paused service.\n"
"\t-c(ommand) num\t: Send command code num to the service.\n"
"\t-v(ersion)\t: Print version and status information.\n"
"\t-h(elp) \t: Show this help\n"
"\tNo arguments\t: Start the service.\n"
1
2
3
4
5
6
7
8
9
10
11
12
四、测试效果
测试程序环境是ubuntu20.04虚拟机,在windows使用socket调试工具创建一个TCP服务器
启动服务,并查看日志,显示服务已运行,外部程序的debug信息也会输出到这个日志文件,查看进程可以看到服务程序test_qtservice和外部程序test_systemd。
外部程序的功能是启动一个TCP客户端,连接到服务器,当收到服务器的消息时自动回复一条confirm消息。
socket工具显示已经有tcp客户端连接到服务端,发送一条消息,收到一条Recv confirm消息。
使用sudo systemctl stop test_systemd.service,停止服务。
五、报错记录
systemd服务不支持启动界面程序,如果启动的外部程序是带界面的则会启动失败,status日志报错:
qt.qpa.xcb: could not connect to display
is inaccessible within this context,qt报错
public QObject,public QtService//如果继承多个类,则分别使用public。public、private、protected三种继承方式的区别。