【问题标题】:How to run C++ TCP/IP multithread server as deamon如何将 C++ TCP/IP 多线程服务器作为守护进程运行
【发布时间】:2021-03-18 17:23:47
【问题描述】:

我在 systemd 中将 TCP/IP 服务器作为守护进程运行时遇到问题。

我有一个服务器,如下所示:https://beej.us/guide/bgnet/html/#client-server-background 每个客户都有自己的流程。

服务器接受新的客户端代码:

    while(1) {  // main accept() loop
        sin_size = sizeof their_addr;
        new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
        if (new_fd == -1) {
            perror("accept");
            continue;
        }

        inet_ntop(their_addr.ss_family,
            get_in_addr((struct sockaddr *)&their_addr),
            s, sizeof s);
        printf("server: got connection from %s\n", s);

        if (!fork()) { // this is the child process
            close(sockfd); // child doesn't need the listener

            // some logic here
            // When the client receives a specific message, it ends. Of course I call close socket and exit with exit code 0.

            close(new_fd);
            exit(0);
        }
        close(new_fd);  // parent doesn't need this
    }

服务:

[Service]
Type=simple
User=root
WorkingDirectory=folder
ExecStart=binary -c config
Restart=always

服务已注册,我使用systemctl 命令控制它。

问题是我的程序中的进程数在客户端连接后逐渐增加。 当我通过命令行运行服务器时,一切正常。

问题应该出在哪里? systemd 守护进程是否需要某些特定配置或客户端进程需要以其他方式终止?

【问题讨论】:

  • 完成后终止是您的应用程序的责任。当您的应用程序完成它应该做的任何事情时,systemd 什么都不知道,也不可能有任何想法。无论出于何种原因,从 systemd 启动时,您的应用程序进程都不会终止。您必须弄清楚原因,只有您拥有对源代码的完全访问权限,并且可以对其进行调试。
  • @SamVarshavchik 应用程序永远不应该退出。它必须监听以接受连接。问题是从命令行与 systemd 守护程序运行的。从命令行它正在工作。如果服务配置正确或需要对我的代码实施 systemd 守护程序的特定行为,我需要建议。
  • 好吧,有些东西正在启动您的应用程序。显示的系统服务是相当基本的,除非您在 systemd 中发现某种错误,否则您必须在您的应用中查找问题。
  • 吹毛求疵:你没有多线程服务器。您有一个多进程服务器。如今,不分叉而是创建线程更容易(而且通常更有效)。无论如何,只需附加到应该已死但未使用gdb 的进程并检查活动调用堆栈。还有简单的ps命令可以告诉进程的状态,也蛮有用的。
  • accept 可能不是最好的系统设计。您给出的示例是您 1980 年的基本设计,但 systemd 有一些称为“套接字激活”的东西。有关详细信息,请参阅0pointer.de/blog/projects/socket-activation.html。每个客户端仍然有一个进程,但 systemd 会为您设置套接字。

标签: c++ c linux tcp systemd


【解决方案1】:

收割你的僵尸。最简单的收割机:

int ignored;
waitpid(-1, &ignored, WNOHANG);
waitpid(-1, &ignored, WNOHANG);
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);

这是可行的,因为任何接受都只会启动一个新进程,但我们每次都会清理两个,因此每次循环时进程数都会减少。您仍然会看到一些僵尸,但不会太多。通常只有一个。

更复杂的收割者可以将僵尸的数量减少到零。 sigselect 可以完成工作。

我不建议为使用次数过多的东西激活套接字,因为每个连接需要支付一个execve()execve() 并不快。

【讨论】:

  • 您也可以使用侦听器套接字激活 systemd 套接字,类似于 inetd 的 wait 模式,但扩展为传递多个侦听器。不过,我仍然不会让我的服务依赖于任何 systemd 接口,因为 systemd 不能移植到非 Linux 系统。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-15
  • 2023-03-02
  • 1970-01-01
  • 2015-07-12
  • 2017-03-31
相关资源
最近更新 更多