skynet源码学习-skynet_daemon守护进程

核心功能与设计目标

  1. 守护进程化:将Skynet进程转为后台服务
  2. 单实例保证:创建并锁定PID文件,防止同一配置重复启动
  3. 环境隔离:脱离终端控制,在后台运行
  4. 资源清理:优雅退出管理

接口详解

  1. 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文件路径

  • 工作流程:
    在这里插入图片描述

  • 关键步骤:

    1. 调用check_pid验证是否已有实例运行
    2. 使用daemon(1,1)创建守护进程
    3. 通过write_pid写入并锁定PID文件
    4. 使用redirect_fds重定向标准输入输出
  1. int daemon_exit(const char *pidfile)
int
daemon_exit(const char *pidfile) {
    return unlink(pidfile); // 删除PID文件
}
  • 功能:清理守护进程资源
  • 调用时机:服务优雅退出时

辅助函数分析

  1. 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文件有效性
  • 逻辑流程:
  1. 打开并读取PID文件
  2. 验证PID格式有效性
  3. 检查PID是否当前进程
  4. 使用kill(pid, 0)探测进程是否存在
  5. 返回运行中进程PID或0
  • 特殊处理:
    • 忽略当前进程自身的PID
    • 使用ESRCH错误码确认进程不存在
  1. 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):非阻塞排他锁,若失败则说明已有实例运行
    • 文件描述符保持打开状态维持锁
  1. 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;
}
  • 功能:重定向标准输入输出
  • 设计目的:
    • 避免后台进程与终端交互
    • 防止日志污染控制台
    • 释放终端资源

设计优势分析

  1. 可靠的单实例保证
    在这里插入图片描述
  • 优势:
    • 文件锁跨进程生效
    • 自动处理进程崩溃残留
    • 支持集群环境部署
  1. 安全的权限控制
  • PID文件权限:0644
int fd = open(pidfile, O_RDWR|O_CREAT, 0644);
  • 意义:
    • 防止未授权修改
    • 允许监控工具读取
    • 避免权限过高导致的安全风险
  1. 跨平台兼容处理
#ifdef __APPLE__
    fprintf(stderr, "'daemon' is deprecated...");
#else
    daemon(1,1);
#endif
  • 优势:
    • macOS特殊提示
    • Linux/Unix标准实现
    • 清晰的弃用警告

在Skynet框架中的作用

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值