skynet源码学习-skynet_daemon守护进程
核心功能与设计目标
- 守护进程化:将Skynet进程转为后台服务
- 单实例保证:创建并锁定PID文件,防止同一配置重复启动
- 环境隔离:脱离终端控制,在后台运行
- 资源清理:优雅退出管理
接口详解
- int daemon_init(const char *pidfile)
int
daemon_init(const char *pidfile) {
int pid = check_pid(pidfile);
if (pid) {
fprintf(stderr, "Skynet is already running, pid = %d.\n", pid);
return 1;
}
#ifdef __APPLE__
fprintf(stderr, "'daemon' is deprecated: first deprecated in OS X 10.5 , use launchd instead.\n");
#else
if (daemon(1,1)) {
fprintf(stderr, "Can't daemonize.\n");
return 1;
}
#endif
pid = write_pid(pidfile);
if (pid == 0) {
return 1;
}
if (redirect_fds()) {
return 1;
}
return 0;
}
-
功能:初始化守护进程
-
参数:pidfile - PID文件路径
-
工作流程:
-
关键步骤:
- 调用check_pid验证是否已有实例运行
- 使用daemon(1,1)创建守护进程
- 通过write_pid写入并锁定PID文件
- 使用redirect_fds重定向标准输入输出
- int daemon_exit(const char *pidfile)
int
daemon_exit(const char *pidfile) {
return unlink(pidfile); // 删除PID文件
}
- 功能:清理守护进程资源
- 调用时机:服务优雅退出时
辅助函数分析
- static int check_pid(const char *pidfile)
static int
check_pid(const char *pidfile) {
int pid = 0;
FILE *f = fopen(pidfile,"r");
if (f == NULL)
return 0;
int n = fscanf(f,"%d", &pid);
fclose(f);
if (n !=1 || pid == 0 || pid == getpid()) {
return 0;
}
if (kill(pid, 0) && errno == ESRCH)
return 0;
return pid;
}
- 功能:检查PID文件有效性
- 逻辑流程:
- 打开并读取PID文件
- 验证PID格式有效性
- 检查PID是否当前进程
- 使用kill(pid, 0)探测进程是否存在
- 返回运行中进程PID或0
- 特殊处理:
- 忽略当前进程自身的PID
- 使用ESRCH错误码确认进程不存在
- static int write_pid(const char *pidfile)
static int
write_pid(const char *pidfile) {
FILE *f;
int pid = 0;
int fd = open(pidfile, O_RDWR|O_CREAT, 0644);
if (fd == -1) {
fprintf(stderr, "Can't create pidfile [%s].\n", pidfile);
return 0;
}
f = fdopen(fd, "w+");
if (f == NULL) {
fprintf(stderr, "Can't open pidfile [%s].\n", pidfile);
return 0;
}
if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
int n = fscanf(f, "%d", &pid);
fclose(f);
if (n != 1) {
fprintf(stderr, "Can't lock and read pidfile.\n");
} else {
fprintf(stderr, "Can't lock pidfile, lock is held by pid %d.\n", pid);
}
return 0;
}
pid = getpid();
if (!fprintf(f,"%d\n", pid)) {
fprintf(stderr, "Can't write pid.\n");
close(fd);
return 0;
}
fflush(f);
return pid;
}
-
功能:写入并锁定PID文件
-
核心机制:
-
关键技术:
- flock(fd, LOCK_EX|LOCK_NB):非阻塞排他锁,若失败则说明已有实例运行
- 文件描述符保持打开状态维持锁
- static int redirect_fds()
static int
redirect_fds() {
int nfd = open("/dev/null", O_RDWR);
if (nfd == -1) {
perror("Unable to open /dev/null: ");
return -1;
}
if (dup2(nfd, 0) < 0) {
perror("Unable to dup2 stdin(0): ");
return -1;
} // stdin
if (dup2(nfd, 1) < 0) {
perror("Unable to dup2 stdout(1): ");
return -1;
} // stdout
if (dup2(nfd, 2) < 0) {
perror("Unable to dup2 stderr(2): ");
return -1;
} // stderr
close(nfd);
return 0;
}
- 功能:重定向标准输入输出
- 设计目的:
- 避免后台进程与终端交互
- 防止日志污染控制台
- 释放终端资源
设计优势分析
- 可靠的单实例保证
- 优势:
- 文件锁跨进程生效
- 自动处理进程崩溃残留
- 支持集群环境部署
- 安全的权限控制
- PID文件权限:0644
int fd = open(pidfile, O_RDWR|O_CREAT, 0644);
- 意义:
- 防止未授权修改
- 允许监控工具读取
- 避免权限过高导致的安全风险
- 跨平台兼容处理
#ifdef __APPLE__
fprintf(stderr, "'daemon' is deprecated...");
#else
daemon(1,1);
#endif
- 优势:
- macOS特殊提示
- Linux/Unix标准实现
- 清晰的弃用警告