我正在尝试从另一个用户空间程序控制 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-test
或 ps 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/