创建一个 Linux5.10 普通 kill 无效的守护进程 Daemon-demo
- 忽略 SIGTERM(15)、SIGHUP(1)等信号,让普通 kill 无效。
- 对 SIGKILL(9)无效 ------ 任何用户态进程都无法阻止 SIGKILL。
- 若想阻止普通用户杀死,可以以 root 运行并改变进程权限,但 root 自己依然可以 kill -9。
下面是一个尽可能防止被普通信号杀死的守护进程例子(Linux 5.10,C语言),它会:忽略 SIGTERM、SIGINT、SIGHUP,每秒输出 hello world 到 syslog。只能通过 kill -9 <pid> 杀死。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
volatile sig_atomic_t keep_running = 1;
void signal_handler(int sig)
{
// 除了 SIGKILL 和 SIGSTOP,这里可以捕获的信号都只是记录日志,并不退出
syslog(LOG_INFO, "Received signal %d, but ignoring it", sig);
}
void setup_signals()
{
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = signal_handler;
// 忽略终止信号,使普通 kill 无效
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
// 不能忽略 SIGKILL 和 SIGSTOP
}
void daemonize()
{
pid_t pid = fork();
if (pid < 0)
exit(EXIT_FAILURE);
if (pid > 0)
exit(EXIT_SUCCESS); // 父进程退出
// 子进程成为会话首领
if (setsid() < 0)
exit(EXIT_FAILURE);
// 忽略 SIGHUP 并再次 fork 确保不是会话首领
signal(SIGHUP, SIG_IGN);
pid = fork();
if (pid < 0)
exit(EXIT_FAILURE);
if (pid > 0)
exit(EXIT_SUCCESS);
// 更改工作目录(忽略返回值,因为这不是关键操作)
if (chdir("/") != 0)
{
// 即使失败也继续,只是记录一下
syslog(LOG_WARNING, "chdir(/) failed");
}
// 重置文件掩码
umask(0);
// 关闭标准文件描述符
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// 重定向到 /dev/null
int fd = open("/dev/null", O_RDWR);
if (fd != -1)
{
dup2(fd, STDIN_FILENO); // stdin
dup2(fd, STDOUT_FILENO); // stdout
dup2(fd, STDERR_FILENO); // stderr
if (fd > 2)
close(fd);
}
}
int main()
{
// 先变成守护进程
daemonize();
// 打开系统日志
openlog("undead_daemon", LOG_PID | LOG_CONS, LOG_DAEMON);
syslog(LOG_INFO, "Daemon started. PID=%d. It cannot be killed by SIGTERM, only kill -9.", getpid());
// 设置信号处理(忽略杀进程信号)
setup_signals();
// 主循环:每秒输出 hello world
while (1)
{
syslog(LOG_INFO, "hello world");
sleep(1);
}
closelog();
return 0;
}