守护进程详解:理论与实践
什么是守护进程?
在 Unix/Linux 系统中,**守护进程(Daemon Process)**是一种长期运行的后台进程,通常不受终端会话控制,适用于服务类任务,如 Web 服务器、数据库管理系统等。
在《Unix 环境高级编程》中,守护进程的特点主要包括:
- 无控制终端:独立于用户的登录会话。
- 后台运行:通常使用
fork()
使其脱离控制终端。 - 会话管理:使用
setsid()
创建新的会话并成为会话首进程。 - 工作目录:通常切换到
/
避免阻止文件系统卸载。 - 文件权限:调整
umask(0)
以确保文件权限可控。 - 日志管理:使用
syslog
记录日志。
守护进程的创建步骤
1. 创建子进程并退出父进程
pid_t pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
} else if (pid > 0) {
exit(EXIT_SUCCESS); // 让父进程退出
}
2. 创建新会话,使进程脱离控制终端
if (setsid() < 0) {
exit(EXIT_FAILURE);
}
3. 处理信号,防止孤儿进程
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);
4. 关闭文件描述符,避免资源占用
for (int i = sysconf(_SC_OPEN_MAX); i >= 0; i--) {
close(i);
}
5. 重定向标准输入、输出和错误到 /dev/null
open("/dev/null", O_RDWR);
dup(0);
dup(0);
完整的守护进程示例
以下是一个完整的 C++ 守护进程示例,支持日志记录和信号处理。
#include <iostream>
#include <fstream>
#include <csignal>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
void signal_handler(int signo) {
if (signo == SIGTERM || signo == SIGINT) {
std::ofstream log("/tmp/daemon.log", std::ios::app);
log << "Daemon terminated" << std::endl;
log.close();
exit(0);
}
}
void daemonize() {
pid_t pid = fork();
if (pid < 0) exit(EXIT_FAILURE);
if (pid > 0) exit(EXIT_SUCCESS);
umask(0);
if (setsid() < 0) exit(EXIT_FAILURE);
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);
pid = fork();
if (pid < 0) exit(EXIT_FAILURE);
if (pid > 0) exit(EXIT_SUCCESS);
chdir("/");
for (int x = sysconf(_SC_OPEN_MAX); x >= 0; x--) close(x);
open("/dev/null", O_RDWR);
dup(0);
dup(0);
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
}
int main() {
daemonize();
while (true) {
std::ofstream log("/tmp/daemon.log", std::ios::app);
log << "Daemon running..." << std::endl;
log.close();
sleep(10);
}
return 0;
}
运行与测试
编译后运行:
g++ daemon.cpp -o daemon
./daemon
查看日志输出:
tail -f /tmp/daemon.log
终止守护进程:
pkill daemon
总结
守护进程是 Unix/Linux 后台服务的基础,理解其原理有助于开发稳定可靠的系统级应用。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 D-Space!