守护进程与服务

一、传统守护进程(5 步曲)

#include <syslog.h>
#include <unistd.h>
#include <fcntl.h>

int main(void) {
    // 1. fork + 父进程退出
    pid_t pid = fork();
    if (pid > 0) exit(0);

    // 2. setsid 创建新会话
    setsid();

    // 3. 再次 fork(可选,防止重新获得控制终端)
    pid = fork();
    if (pid > 0) exit(0);

    // 4. 切换工作目录
    chdir("/");
    umask(0);

    // 5. 关闭 / 重定向标准 fd
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    open("/dev/null", O_RDONLY);
    open("/tmp/myd.log", O_WRONLY|O_CREAT|O_APPEND, 0644);
    dup2(STDOUT_FILENO, STDERR_FILENO);

    // 6. 业务
    openlog("myd", LOG_PID, LOG_DAEMON);
    syslog(LOG_INFO, "started");

    while (1) {
        syslog(LOG_INFO, "tick");
        sleep(1);
    }
}

二、systemd 服务(推荐)

1. 创建 unit 文件 /etc/systemd/system/myapp.service

[Unit]
Description=My Application
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/myapp --config /etc/myapp.conf
Restart=on-failure
RestartSec=5
User=appuser
Group=appgroup
WorkingDirectory=/var/lib/myapp
Environment=LOG_LEVEL=info
EnvironmentFile=-/etc/myapp.env

StandardOutput=journal
StandardError=journal

# 资源限制
LimitNOFILE=65536
CPUQuota=50%
MemoryMax=128M

# 安全
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/myapp /var/log/myapp

[Install]
WantedBy=multi-user.target

2. 管理命令

systemctl daemon-reload
systemctl enable myapp.service
systemctl start  myapp.service
systemctl status myapp.service
systemctl stop   myapp.service
journalctl -u myapp.service -f

3. Type 类型

4. notify 模式

#include <systemd/sd-daemon.h>

sd_notify(0, "READY=1\nSTATUS=Running\n");
// 在 watchdog 模式下周期发送
sd_notify(0, "WATCHDOG=1");

三、init 程序

1. BusyBox init

::sysinit:/etc/init.d/rcS
::respawn:/usr/bin/myapp
::ctrlaltdel:/sbin/reboot
::shutdown:/etc/init.d/rcK

2. Buildroot / Yocto 自带

四、信号处理与优雅退出

volatile sig_atomic_t g_stop = 0;
void on_signal(int sig) { g_stop = 1; }

int main(void) {
    signal(SIGINT,  on_signal);
    signal(SIGTERM, on_signal);
    signal(SIGPIPE, SIG_IGN);

    while (!g_stop) {
        do_work();
    }

    cleanup();
    return 0;
}

五、看门狗

#include <linux/watchdog.h>

int fd = open("/dev/watchdog", O_WRONLY);
ioctl(fd, WDIOC_SETTIMEOUT, &(int){30});   // 30s 超时
while (!stop) {
    ioctl(fd, WDIOC_KEEPALIVE, 0);          // 喂狗
    sleep(10);
}

六、嵌入式开机自启

1. /etc/init.d/ 脚本(SysV init)

#!/bin/sh
### BEGIN INIT INFO
# Provides:          myapp
# Required-Start:    $local_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
### END INIT INFO
case "$1" in
    start) /usr/bin/myapp & ;;
    stop)  killall myapp ;;
    *)     echo "Usage: $0 {start|stop}"; exit 1 ;;
esac

2. Buildroot 应用启动

七、健康检查与监控