QT实现后台服务,linux下使用systemd管理QT后台服务

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 try­restart name.service

重载或重启服务,先加载再启动:systemctl reload­or­restart name.service

重载或条件式重启:systemctl reload­or­try­restart name.service

禁止自动和手动启动:systemctl mask name.service

取消禁止:systemctl unmask name.service

查看某服务当前激活与否的状态:systemctl is­active name.service

查看所有已经激活的服务:systemctl list­units ­t service

查看所有服务:systemctl list­units ­t service ­a

设定某服务开机自启动:systemctl enable name.service

设定某服务开机禁止启动:systemctl disable name.service

查看所有服务的开机自启动状态:systemctl list­unit­files –t service

列出该服务在哪些运行级别下启用和禁止:ls /etc/systemd/system/*.wants/sshd.service

查看服务是否开机启动:systemctl is­enabled name.service

查看服务的依赖关系:systemctl list­dependencies 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三种继承方式的区别。
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蝈蝈(GuoGuo)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值