Linux 守护进程创建流程 7 步详解从 fork 到信号处理的完整代码实现1. 守护进程的核心特征与应用场景守护进程Daemon是Linux系统中一类特殊的后台服务进程它们通常具有以下典型特征生命周期长从系统启动时开始运行直到系统关闭才终止无控制终端脱离终端控制不会接收终端输入或产生终端输出独立会话自成会话组和进程组通常以root权限运行命名惯例进程名通常以d结尾如sshd、httpd在实际系统管理中守护进程承担着关键角色$ ps -efj | grep -E [[:alnum:]]d$ root 1 0 0 2025 ? 00:00:01 /sbin/init root 456 1 0 2025 ? 00:00:23 /usr/sbin/sshd mysql 1024 1 0 2025 ? 00:01:45 /usr/sbin/mysqld2. 守护进程创建的完整步骤解析2.1 第一次fork脱离终端控制pid_t pid fork(); if (pid 0) { perror(fork); exit(EXIT_FAILURE); } else if (pid 0) { exit(EXIT_SUCCESS); // 父进程退出 }关键原理父进程退出使子进程成为孤儿进程Shell认为命令已执行完毕可继续输入新命令子进程在后台继续运行PPID变为1init进程2.2 setsid创建新会话if (setsid() 0) { perror(setsid); exit(EXIT_FAILURE); }会话控制相关概念概念描述相关系统调用进程组一组相关进程的集合setpgid, getpgid会话一个或多个进程组的集合setsid, getsid控制终端会话与终端设备的关联tcsetpgrp2.3 第二次fork确保非会话首进程pid fork(); if (pid 0) { perror(fork); exit(EXIT_FAILURE); } else if (pid 0) { exit(EXIT_SUCCESS); // 子进程退出 }必要性分析防止会话首进程意外获取终端如打开终端设备文件确保守护进程永远不会获得控制终端2.4 设置工作目录if (chdir(/) 0) { perror(chdir); exit(EXIT_FAILURE); }目录选择建议/通用选择确保文件系统可卸载/tmp适合需要写临时文件的守护进程专用目录如MySQL使用/var/lib/mysql2.5 重设文件权限掩码umask(0);umask值的影响原始权限umask实际权限06660220644077702707502.6 关闭文件描述符for (int fd sysconf(_SC_OPEN_MAX); fd 0; fd--) { close(fd); } // 重定向标准流到/dev/null open(/dev/null, O_RDWR); // stdin dup(0); // stdout dup(0); // stderr文件描述符处理策略关闭所有非必要描述符重定向标准输入输出到/dev/null保留必要的日志文件描述符2.7 信号处理机制struct sigaction sa; sa.sa_handler SIG_IGN; sigemptyset(sa.sa_mask); sa.sa_flags 0; // 忽略子进程终止信号 if (sigaction(SIGCHLD, sa, NULL) 0) { perror(sigaction); exit(EXIT_FAILURE); } // 自定义信号处理 sa.sa_handler handle_signal; if (sigaction(SIGTERM, sa, NULL) 0) { perror(sigaction); exit(EXIT_FAILURE); }关键信号处理建议信号默认动作守护进程处理建议SIGHUP终止重载配置文件SIGTERM终止优雅关闭处理SIGCHLD忽略避免僵尸进程SIGUSR1终止自定义功能触发3. 完整代码实现与测试3.1 守护进程模板代码#include stdio.h #include stdlib.h #include unistd.h #include signal.h #include sys/stat.h #include fcntl.h #include time.h #include string.h #define LOCK_FILE /var/run/daemon_example.pid #define LOG_FILE /var/log/daemon_example.log volatile sig_atomic_t running 1; void signal_handler(int sig) { switch(sig) { case SIGTERM: case SIGINT: running 0; break; case SIGHUP: // 重载配置 break; } } int daemonize() { // 第一次fork pid_t pid fork(); if (pid 0) return -1; if (pid 0) exit(EXIT_SUCCESS); // 父进程退出 // 创建新会话 if (setsid() 0) return -1; // 第二次fork pid fork(); if (pid 0) return -1; if (pid 0) exit(EXIT_SUCCESS); // 设置工作目录 if (chdir(/) 0) return -1; // 设置文件权限掩码 umask(0); // 关闭文件描述符 for (int fd sysconf(_SC_OPEN_MAX); fd 0; fd--) { close(fd); } // 重定向标准流 open(/dev/null, O_RDWR); // stdin dup(0); // stdout dup(0); // stderr // 创建PID文件 int lock_fd open(LOCK_FILE, O_RDWR|O_CREAT, 0640); if (lock_fd 0) return -1; if (lockf(lock_fd, F_TLOCK, 0) 0) { close(lock_fd); return -1; // 已存在运行实例 } char pid_str[16]; snprintf(pid_str, sizeof(pid_str), %d\n, getpid()); write(lock_fd, pid_str, strlen(pid_str)); // 设置信号处理 struct sigaction sa; sa.sa_handler signal_handler; sigemptyset(sa.sa_mask); sa.sa_flags 0; sigaction(SIGTERM, sa, NULL); sigaction(SIGINT, sa, NULL); sigaction(SIGHUP, sa, NULL); sa.sa_handler SIG_IGN; sigaction(SIGCHLD, sa, NULL); return 0; } int main() { if (daemonize() 0) { fprintf(stderr, Failed to daemonize\n); return EXIT_FAILURE; } int log_fd open(LOG_FILE, O_WRONLY|O_CREAT|O_APPEND, 0640); if (log_fd 0) { return EXIT_FAILURE; } while (running) { time_t now time(NULL); char *timestamp ctime(now); write(log_fd, timestamp, strlen(timestamp)); sleep(60); } close(log_fd); unlink(LOCK_FILE); return EXIT_SUCCESS; }3.2 编译与测试方法# 编译 gcc -o daemon_example daemon_example.c # 启动守护进程 sudo ./daemon_example # 检查进程状态 ps -ef | grep daemon_example # 查看日志 tail -f /var/log/daemon_example.log # 停止守护进程 sudo kill $(cat /var/run/daemon_example.pid)4. 高级话题与最佳实践4.1 使用daemon()函数简化Linux提供了daemon()库函数简化创建过程#include unistd.h int daemon(int nochdir, int noclose);参数说明nochdir0表示切换到根目录noclose0表示重定向标准流到/dev/null注意事项非POSIX标准不同系统实现可能不同某些实现可能不进行第二次fork4.2 现代系统管理工具集成Systemd服务单元示例[Unit] DescriptionExample Daemon Afternetwork.target [Service] Typeforking PIDFile/var/run/daemon_example.pid ExecStart/usr/sbin/daemon_example ExecReload/bin/kill -HUP $MAINPID ExecStop/bin/kill -TERM $MAINPID [Install] WantedBymulti-user.target管理命令sudo systemctl daemon-reload sudo systemctl start daemon_example sudo systemctl status daemon_example4.3 常见问题排查技巧问题现象守护进程意外退出检查系统日志/var/log/syslog验证资源限制ulimit -a确认依赖服务可用性问题现象产生僵尸进程确保正确处理SIGCHLD信号使用waitpid回收子进程问题现象无法写入日志检查目标目录权限验证磁盘空间df -h确认inode未耗尽df -i