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

本文介绍了如何使用QT的QtService库实现跨平台后台服务,并详细讲解了Linux下systemd的使用,包括systemctl常用命令、unit配置文件的解析,以及如何用systemd管理Qt写的后台程序。在Ubuntu系统中进行了测试,展示了服务的启动和停止操作,同时记录了可能出现的错误及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、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)

添加新文件:ubuntuservice.hubuntuservice.cpp,服务功能主要由重写的几个虚函数完成:start、stop、pause、resume,在启动、停止、暂停、重启服务时会自动调用这些虚函数。

修改 main.cpp

#include "ubuntuservice.h"

int main(int argc, char *argv[])
{
    UbuntuService service(argc, argv);

    return service.exec();
}

ubuntuservice.h 内容如下:

#ifndef UBUNTUSERVICE_H
#define UBUNTUSERVICE_H

#include <QCoreApplication>
#include "qtservice.h"

class UbuntuService : public QObject ,public QtService<QCoreApplication>
{
    Q_OBJECT
public:
    explicit UbuntuService(int argc, char **argv);
    ~UbuntuService();


protected:
    void start()override;   //开始服务
    void stop()override;    //停止服务
    void pause()override;   //暂停服务
    void resume()override;  //重启服务

signals:


private:

};

#endif // UBUNTUSERVICE_H

ubuntuservice.cpp 内容如下:

#include "ubuntuservice.h"
#include <QDebug>

UbuntuService::UbuntuService(int argc, char **argv)
    : QtService<QCoreApplication>(argc, argv, "QtDbusSensorService")
{
        setServiceDescription("QtDbusSensorService");

        setServiceFlags(QtServiceBase::CanBeSuspended);
}

UbuntuService::~UbuntuService()
{

}

///开始服务
void UbuntuService::start()
{
    qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"ubuntuService";
}

///停止服务
void UbuntuService::stop()
{
    qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"ubuntuService";

}

///暂停服务
void UbuntuService::pause()
{
    qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"ubuntuService";

}

///暂停服务
void UbuntuService::resume()
{
    qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"ubuntuService";

}

服务启动后会自动进入到 start() 这个虚函数中,其他几个函数同理,那么在实际项目中, 只需要将自己要执行的内容放到 start()中就可以了。

二、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 #查看是否开机启动

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 enablesystemctl 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 enablesystemctl disable 时会同时 enable 和 disable 的其它 unit 列表

三、systemd管理Qt写的后台程序

新建一个服务 unit 文件

cd /usr/lib/systemd/system/
sudo vim test_systemd.service

编写unit文件

[Unit]
Description=test_systemd Service                                              
[Service]
Type=forking
ExecStart=/bin/bash /home/fl/release/test_qtservice.sh
ExecStop=/bin/bash /home/fl/release/test_qtservice_quit.sh
StandardOutput=syslog
StandardError=inherit
[Install]
WantedBy=multi-user.target

将服务注册到系统

systemctl enable test_systemd.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/#上一步注册是在这个文件夹创建软链接

ExecStartExecStop是两个脚本文件,分别对应启动服务和停止服务

test_qtservice.sh

#!/bin/sh
workdir=$(cd $(dirname $0); pwd)
export LD_LIBRARY_PATH=$workdir
cd $workdir
./test_qtservice -s

test_qtservice_quit.sh

#!/bin/sh
workdir=$(cd $(dirname $0); pwd)
export LD_LIBRARY_PATH=$workdir
cd $workdir
./test_qtservice -t

test_qtservice是使用QtService写的服务程序,后面跟-s代表启动服务,-t代表停止服务,也就是会调用QtService的startstop虚函数。
其他含义:

-[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"

四、测试效果

测试程序环境是基于Ubuntu的 openKylin 0.9.5 版本系统虚拟机

启动服务,并查看日志,显示服务已运行, 查看进程可以看到服务程序test_qtservice
在这里插入图片描述

使用 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三种继承方式的区别。

/下面的报错是直接运行导致,或者win系统下直接运行,建议直接构建出来即可,不直接运行的可不看
如果想直接通过QtCreator运行服务,你会看到如下报错:

The service QtServiceDemo could not

别着急,这不是代码的问题,这是因为服务的启动需要添加一个启动参数,-exec 或者更简单点 -e

如果要在Qt Creator中直接启动的话,那只需要配置一下启动参数即可,如下:
在这里插入图片描述
再次运行就可以了。

通常,后台服务程序都是由一个主程序去调用启动,如果是在Qt主程序中调用,可以使用QProcess完成,也是要添加启动参数才行。


Win系统下如果要直接双击运行的话,由于需要运行参数,所以不能直接双击打开执行程序,可以通过一个脚本调用来打开:

autorun.bat

@echo off
start QtServiceDemo.exe -exec 
exit
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值