创建一个 Linux5.10 普通 kill 无效的守护进程 Daemon-demo

创建一个 Linux5.10 普通 kill 无效的守护进程 Daemon-demo

  1. 忽略 SIGTERM(15)、SIGHUP(1)等信号,让普通 kill 无效。
  2. 对 SIGKILL(9)无效 ------ 任何用户态进程都无法阻止 SIGKILL。
  3. 若想阻止普通用户杀死,可以以 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;
}
相关推荐
SuperByteMaster5 小时前
timer.c 介绍
c语言
qq_448011167 小时前
C语言中的柔性数组
c语言·开发语言·柔性数组
luj_176810 小时前
塔防牌:策略与卡牌的智慧碰撞
服务器·c语言·开发语言·经验分享·算法
wuyk55510 小时前
3.链表:用指针串联的动态数据结构
c语言·开发语言·数据结构·链表
Lzh编程小栈11 小时前
【嵌入式底层】SPI超透彻详解:通信原理、四线结构、四种模式、寄存器底层 + 面试全集
c语言·stm32·单片机·面试·职场和发展
凉茶钱12 小时前
【数据结构】堆的应用
c语言·数据结构
Herbert_hwt16 小时前
【C语言基础】常量、选择结构与运算符全解析
c语言·开发语言·算法
WWJA王文举1 天前
I²C通信完整流程详解:START、地址、ACK、数据、Repeated START和STOP一次讲透
c语言·开发语言
qq_448011161 天前
C语言中的动态内存分配
c语言·开发语言·php
动词ing1 天前
【学习笔记】C语言(数组指针与指针数组+字符数组+函数+参数传递+字符串作为形参+递归函数+指针函数+回调函数+结构体嵌套+内存动态分配函数)
c语言·笔记·学习