守护进程与服务
一、传统守护进程(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 类型
simple:默认,主进程是服务
forking:fork 后父退出
oneshot:启动一次,退出后状态为 inactive
notify:通过 sd_notify 通知"已就绪"
dbus:通过 D-Bus 通知
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 自带
systemd 或 busybox init 或 sysvinit
四、信号处理与优雅退出
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 应用启动
/etc/init.d/S99myapp (数字 = 启动顺序)
七、健康检查与监控
- prometheus exporter
- 串口 / syslog 输出心跳
- 看门狗硬件复位