Ubuntu 16.10中自定义C守护进程systemctl启动失败如何解决?
解决Daemon1.service启动失败的问题
咱们一步步排查并修复你的问题:
1. 修复C守护进程代码的核心问题
你的代码存在几个不符合守护进程规范的地方,直接导致systemd判定启动失败:
错误点1:父进程错误的退出码
当fork()返回pid>0时,父进程调用exit(1)——这会让systemd认为启动失败(非0退出码在systemd逻辑里代表启动出错)。对于Type=forking的服务,父进程必须正常退出(exit(0)),以此告诉systemd子进程已经成功启动并接管后续运行。
错误点2:缺少守护进程的标准初始化步骤
一个合格的守护进程需要完成一系列步骤来彻底脱离终端和会话关联:
- 调用
setsid()创建新会话,脱离原控制终端 - 再次fork避免成为会话组长(防止后续意外打开终端)
- 关闭标准输入、输出、错误流(避免占用终端资源,防止输出干扰systemd判断)
- 使用覆盖模式写入PID文件(避免多次启动后文件中存在多个PID,导致systemd无法识别有效进程)
修改后的C代码:
#include<stdio.h> #include<sys/types.h> #include<stdlib.h> #include<unistd.h> #include <sys/stat.h> #include <fcntl.h> int main(){ pid_t pid; // 第一次fork,脱离父进程 pid = fork(); if(pid > 0){ // 父进程正常退出,告知systemd启动成功 exit(0); } else if(pid < 0){ perror("fork failed"); exit(1); } // 创建新会话,彻底脱离原终端 if(setsid() == -1){ perror("setsid failed"); exit(1); } // 第二次fork,避免成为会话组长,防止意外绑定终端 pid = fork(); if(pid > 0){ exit(0); } else if(pid < 0){ perror("second fork failed"); exit(1); } // 设置umask为0,确保后续创建文件的权限符合预期 umask(0); // 切换到根目录(可选,避免进程所在目录被挂载点占用导致无法卸载) chdir("/"); // 关闭标准输入、输出、错误流 close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); // 用/dev/null替代标准流,防止后续代码误操作触发错误 open("/dev/null", O_RDONLY); open("/dev/null", O_WRONLY); open("/dev/null", O_WRONLY); // 写入PID文件,使用覆盖模式确保文件中只有当前进程的PID FILE *fp = fopen("pid.pid", "w"); if(fp == NULL){ perror("fopen failed"); exit(1); } fprintf(fp, "%d", getpid()); fclose(fp); // 守护进程主循环,添加sleep避免占用过多CPU资源 while(1){ sleep(1); // 这里可以添加你的业务逻辑 } return 0; }
2. 优化systemd服务配置
你的Daemon1.service配置有几个需要调整的细节:
- 先注释
Restart=always(测试阶段如果启动失败,该配置会触发快速重启,直接触发systemd的启动次数限制) - 若后续需要自动重启,建议添加
RestartSec=5,避免快速重启触发限制
修改后的Daemon1.service:
[Unit] Description=Socket programming with Daemon [Service] User=root Type=forking WorkingDirectory=/Omkar/Doc/systemctl/ ExecStart=/Omkar/Doc/systemctl/main # 先注释自动重启,测试成功后再开启 # Restart=always # RestartSec=5 PIDFile=/Omkar/Doc/systemctl/pid.pid [Install] WantedBy=multi-user.target
3. 重新部署并测试
执行以下命令完成部署和验证:
# 重新编译修改后的C代码 gcc main.c -o main # 将可执行文件放到指定目录(如果之前没放对的话) cp main /Omkar/Doc/systemctl/ # 重新加载systemd配置 systemctl daemon-reload # 停止之前处于失败状态的服务 systemctl stop Daemon1.service # 启动服务 systemctl start Daemon1.service # 查看服务状态 systemctl status Daemon1.service
如果一切正常,你会看到服务状态显示为active (running)。
失败原因复盘
- 父进程
exit(1)让systemd判定启动失败,加上Restart=always的配置,触发了快速循环重启 - 多次快速重启触发了systemd的启动次数限制,最终导致服务被标记为
start-limit-hit - PID文件使用追加模式,多次启动后文件中存在多个PID,systemd无法正确识别有效子进程
内容的提问来源于stack exchange,提问作者Omkar
相关产品推荐
相关产品推荐

