进一步阅读

进一步阅读

当我重新启动 Raspberry Pi(Stretch)时,守护进程因/run/user/1000不存在而无法启动。这是我的单位文件:

[Unit]
Description=SBFspot Upload Daemon

[Service]
User=pi
Type=forking
TimeoutStopSec=60
ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon -p /run/user/1000/sbfspotupload.pid 2>&1> /dev/null
PIDFile=/run/user/1000/sbfspotupload.pid
Restart=no
RestartSec=15
SuccessExitStatus=SIGKILL

[Install]
WantedBy=default.target

当我配置为Restart=on-failure在几次重试后一切顺利时,但这并不是我真正想要的。我希望守护进程等待/run/user/1000安装。我尝试过After=run-user-1000.mount,但仍然失败。

这是可能的还是我必须坚持Restart=on-failure

答案1

/run/user/1000当然,在用户 #1000 登录或显式启动 xyr 每用户服务管理之前,它并不存在,这是一个转移注意力的内容。使用它的整个机制不应该存在。

该程序的错误#215比你想象的要深得多。这个服务单元文件是非常错误的,程序本身的运行也是如此。有很多货物狂热编程是基于没有真正理解 systemd 服务单元的基础知识。

  • 服务单元不是 shell 脚本。 系统手册解释一下。此处的设置ExecStart会导致服务程序以一些额外的参数运行,2>&1>并且/dev/null.
  • 服务管理器已经确保只有一项服务运行。 这里添加的所有代码都是不必要的垃圾。
  • 摇摇欲坠且危险的PID文件机制应该不是使用。 它在适当的服务管理中没有地位。
  • 服务管理器还处理在守护进程上下文中调用服务。 很多其他代码main()基于恶魔化谬误的不必要的垃圾。
    • 程序fork()根本不应该是 ing,并且服务就绪机制不应该指定为Type=forking。就像现实世界中的许多程序一样,这个程序是不是首先谈谈分叉准备协议。
    • 该计划是已经以超级用户身份运行。 User=root是不必要的,确实应该重新设计服务不是需要以超级用户权限运行,而是在专用的非特权服务帐户的支持下运行。
    • 服务经理是已经记录标准输出和错误,并且比这个程序做得更好。这个自行开发的日志系统只会增加一个日志文件,直到填满整个文件系统,从而消耗为超级用户保留的所有紧急空间。
    • 您的日志只是标准错误,可以从 C++ 轻松访问为std::clog.
    • 实际上,fork()从到标准错误重定向的所有代码不应该使用。服务管理手柄全部其中,从会话领导到工作目录和 umask 到标准 I/O,并且做得正确。这个程序没有,也不应该尝试这样做任何当在服务管理器下使用时,它本身就是这样的。

      你从 Boost 获取的一切都是错误的。

  • 三个服务单元是不必要的维护开销。 它们仅在After设置上有所不同,并且可以将它们合并为一个。
  • 无礼终止并不是成功。 鉴于有已经终止时清理文件的一个问题SuccessExitStatus=SIGKILL是错误的。正常终止应该是优雅的,通过SIGTERM,并且SIGKILL应该被认为是异常的。 (当然,output正如已经解释的那样,整个文件机制是一个实现得很糟糕的本地日志机制,不应该在服务管理下使用。)这是 systemd 的默认设置。
  • 数据库对象和其他内容的析构函数应该运行。 不要main()离开exit()

正确实现并在服务管理器下运行的守护程序(无论是来自 daemontools、runit、s6、nosh、systemd 还是其他东西)要短得多:

……// 到目前为止都一样
无效pvo_上传(无效)
{
    std::clog << "启动守护进程..." << std::endl;

    公共服务代码();

    std::clog << "停止守护进程..." << std::endl;
}

int main(int argc, char *argv[])
{
    整数c;
    const char *config_file = "";

    /* 解析命令行 */
    同时(1)
    {
        静态结构选项 long_options[] =
        {
            {“配置文件”,required_argument,0,'c'},
            { 0, 0, 0, 0 }
        };

        int 选项索引 = 0;
        c = getopt_long (argc, argv, "c:", long_options, &option_index);

        if (c == -1) 中断;

        开关(c)
        {
            案例“c”:
                配置文件=optarg;
                休息;
            默认:
                返回 EXIT_FAILURE;
                休息;
        }
    }

    if (cfg.readSettings(argv[0], config_file) != 配置::CFG_OK)
        返回 EXIT_FAILURE;

    std::clog <<“正在启动 SBFspotUploadDaemon 版本”<< 版本 << std::endl;

    // 检查数据库是否可访问
    db_SQL_Base db = db_SQL_Base();
    db.open(cfg.getSqlHostname(), cfg.getSqlUsername(), cfg.getSqlPassword(), cfg.getSqlDatabase());
    if (!db.isopen())
    {
        std::clog <<“无法打开数据库。检查配置。” << std::endl;
        返回 EXIT_FAILURE;
    }

    // 检查数据库版本
    int schema_version = 0;
    db.get_config(SQL_SCHEMAVERSION, schema_version);
    db.close();

    if (schema_version < SQL_MINIMUM_SCHEMA_VERSION)
    {
        std::clog << "将数据库升级到版本 " << SQL_MINIMUM_SCHEMA_VERSION << std::endl;
        返回 EXIT_FAILURE;
    }

    // 安装我们的信号处理程序。
    // 这响应服务管理器发出的服务停止信号。
    信号(SIGTERM,处理程序);

    // 启动守护进程循环
    pvo_upload();

    返回退出_成功;
}

而且服务单元也更短:

[单元]
描述=SBFspot 上传守护进程
After=mysql.service mariadb.service network.target

[服务]
类型=简单
超时停止秒=10
ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon
重新启动=成功

[安装]
WantedBy=多用户.target

systemctl status可以使用和查看日志输出journalctl-u如果需要,可以使用选项和服务名称)。

进一步阅读

相关内容