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

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

– 简单的C守护进程

这个守护进程只是一个C程序,它通过syslog每秒守护自己并记录一条消息。

#include  #include  #include  #include  #include  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  #include  #include  #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 ? 它为您提供了更多的灵活性。

现在,回答你的问题:

我的问题是检测守护程序已被停止。

为此,您必须在父/控制进程中侦听SIGCHLD信号。 这很好,因为您直接调用了该过程。 但是如果你调用一个shell脚本然后分叉你的守护进程,那就很难了。 这就是为什么大多数守护进程都写了一个叫做pid文件的东西 – 守护进程早期编写的文件,它的PID是该文件中唯一的内容。 通常,人们会把它/ /tmp/mydaemon.pid或类似的东西。

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

像这样的东西:

 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等,以便更好地了解它们如何更详细地实现这一点。

您可以在守护程序中运行套接字服务器,然后将控制客户端用作普通CLI。 CLI将测试消息或控制命令发送给守护程序,守护程序提供响应。 根据守护进程的响应,CLI可以监视守护进程的状态并对其进行控制。