从另一个程序控制 C 守护进程

标签 c linux daemon interprocess

我正在尝试从另一个用户空间程序控制 C 守护程序。

- 简单的 C 守护程序

这个守护进程只是一个 C 程序,它 self 守护并通过系统日志每秒记录一条消息。

#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>

void bye();

int main()
{
  printf("Daemon starting ...\n");
  openlog("daemon-test", LOG_PID, LOG_DAEMON);
  signal(SIGTERM, bye);

  if(0 != daemon(0, 0))
  {
    syslog(LOG_ERR, "Can't daemonize\n");
    return EXIT_FAILURE;
  }

  syslog(LOG_INFO, "Daemon started !\n");

  while(1)
  {
    syslog(LOG_INFO, "Daemon alive\n");
    sleep(1);
  }

  return EXIT_SUCCESS;
}

void bye()
{
  syslog(LOG_INFO, "Daemon killed !\n");
  exit(EXIT_SUCCESS);
}

- 从 C 测试程序启动和终止守护程序

出于测试目的,我开发了一个最小示例。我正在使用 popen 启动守护进程,因为我希望我的程序继续执行。

5 秒后,测试程序应该会终止守护进程。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#define DAEMON_NAME       "daemon-test"

int main()
{
    FILE* pipe = NULL;
    int i = 0;

    printf("Launching '%s' program\n", DAEMON_NAME);
    if(NULL == (pipe = popen(DAEMON_NAME, "re")))
    {
        printf("An error occured launching '%s': %m\n", DAEMON_NAME);
        return EXIT_FAILURE;
    }
    printf("Program '%s' launched\n", DAEMON_NAME);

    while(i<5)
    {
        printf("Program alive !\n");
        sleep(1);
        i++;
    }

    if(NULL == (pipe = popen("killall " DAEMON_NAME, "re")))
    {
        printf("An error occured killing '%s' program: %m\n", DAEMON_NAME);
        return EXIT_FAILURE;
    }
    printf("Program '%s' killed\n", DAEMON_NAME);

    return EXIT_SUCCESS;
}

测试程序日志:

$ ./popenTest 
Launching 'daemon-test' program
Program 'daemon-test' launched
Program alive !
Program alive !
Program alive !
Program alive !
Program alive !
Program 'daemon-test' killed

系统日志:

Jun 25 13:58:15 PC325 daemon-test[4445]: Daemon started !
Jun 25 13:58:15 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:16 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:17 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:18 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:19 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:20 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:20 PC325 daemon-test[4445]: Daemon killed !

所以我可以从我的 C 程序启动和终止守护进程,但是我想在某些特定情况下改进行为。

- 处理守护进程崩溃

守护进程可能会在某个时候失败,在这种情况下,应该通知控制程序以便它可以重新启动。我的问题是检测到守护进程已停止。

虽然我想通过调用 pclose 来启动一个等待守护进程终止的线程,但是它不会工作,因为守护进程已经关闭了文件描述符并分离了进程。

所以我正在寻找在守护程序退出时通知程序的最佳方法。

我可以使用带有 exec 系列的 linux 调用进行轮询(例如 pgrep daemon-testps aux | grep daemon-test)但是我认为有更有效的方法可以实现这一目标。

- 处理测试程序错误

如果测试程序在杀死守护进程之前被杀死或失败,则在下一次执行时,守护进程的两个实例将同时运行。

测试程序日志:

$ ./popenTest 
Launching 'daemon-test' program
Program 'daemon-test' launched
Program alive !
Program alive !
Program alive !
^C
$ ./popenTest 
Launching 'daemon-test' program
Program 'daemon-test' launched
Program alive !
Program alive !
Program alive !
Program alive !
Program alive !
Program 'daemon-test' killed

系统日志:

Jun 25 14:17:25 PC325 daemon-test[4543]: Daemon started !
Jun 25 14:17:25 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:26 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:27 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:28 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:29 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:29 PC325 daemon-test[4547]: Daemon started !
Jun 25 14:17:29 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:30 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:30 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:31 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:31 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:32 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:32 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:33 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:33 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:34 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:34 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:34 PC325 daemon-test[4543]: Daemon killed !
Jun 25 14:17:34 PC325 daemon-test[4547]: Daemon killed !

我想通过检查是否已经有守护进程实例在运行来避免这种情况。如果没有,我可以从控制程序启动守护进程。

否则,如果守护程序的一个或多个实例正在运行,我将在启动新的之前杀死它们。

这可以通过调用 killall daemon-test 来实现,但是在每次执行时调用这个命令并不能让我满意,因为它在大多数时候都没有用。

此外,我想明确记录每次执行的情况,尽管我想知道在这种情况下到底有多少实例在运行。

再一次,这可以使用 linux 命令调用轻松解决,但我正在寻找最有效的方法。

有人知道我如何在不依赖 linux 命令调用的情况下实现守护进程控制吗?


编辑: 2018 年 6 月 26 日

我应该从一开始就把它精确化,但我的目标是能够监控守护进程不必修改它的代码。

因此守护进程不会将其 pid 写入文件,并且始终与其调用者分离。

最佳答案

而不是通过 popen 运行程序为什么不使用旧的 POSIX fork + exec ?它为您提供了更多的灵 active 。

现在,回答你的问题:

My problem is to detect that the daemon has been stopped.

为此你必须听SIGCHLD在您的 parent /控制过程中发出信号。这已经足够好了,因为您直接调用了流程。但是,如果您调用一个 shell 脚本,然后 fork 您的守护进程,就会变得困难。这就是为什么大多数守护进程会写一个叫做 pid 的东西的原因。 file - 守护进程早期写入的文件,其 PID 作为该文件中的唯一内容。通常,人们会这样说 /tmp/mydaemon.pid或类似的东西。

在 Linux 上,您的控制进程可以从该文件中读取 PID,然后您可以每秒测试是否 /proc/<pid>/exe文件已存在。如果没有,你就知道守护进程死了。例如,如果您的子程序的 PID 是 1234,那么 /proc/1234/exe将是一个指向子程序可执行文件实际位置的软链接(soft link)。

像这样:

FILE *f;
pid_t pid_child;
char proc_path[256];

f = fopen("/tmp/mydaemon.pid", "r");
fscanf(f, "%d", &pid_child);
fclose(f);
sprintf(proc_path, "/proc/%d/exe", pid_child);

while(1) {
    if (access(proc_path, F_OK) == 0) {
        printf("Program alive !\n");
        sleep(1);
    } else {
        printf("Program dead!\n");
        break;
    }
}

其实这大概就是实现了多少个init系统。请参阅 rc、systemd、upstart 等,以更好地了解他们如何更详细地实现这一点。

关于从另一个程序控制 C 守护进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51024183/

相关文章:

带有 struct 和 fgets 的 C 程序

c++ - 控制不同的硬件 : multithreading or something different?

c - 替换 Vim 中的项目而不替换旧的替换项目

python - *nix 系统中 python 中的自重启守护进程

c - 在结构体数组内分配结构体数组

c - 用于微 Controller 的 GUI 库

linux - 使用变量作为命令的输入

python - 守护进程意外死亡

node.js - 如何启动新的nodejs进程

c - 在 strncpy 或类似工具上使用 memcpy 是不好的做法吗?