You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

守护进程PID写入文件失败问题求助(附代码)

问题:fprintf无法将守护进程PID写入文件

我尝试将守护进程的PID写入文件,使用fprintf实现但未能成功。以下是我的代码:

int write_pid_in_file(struct config *conf, pid_t pid)
{
    //conf->global->pid_file = /tmp/HTTPd.pid
    FILE *file_opened = fopen(conf->global->pid_file,"w+");
    if (!file_opened)
        return 2;
    printf("%d\n",pid); //check if it's the good pid
    fprintf(file_opened, "%d\n",pid);
    fclose(file_opened);
    return 0;
}

在如下函数中我调用了上述写入函数,并从守护进程获取PID:

int daemon_start(struct config *conf)
{
    pid_t cpid;

    cpid = fork();

    if (cpid == -1)
    {
        perror("fork");
        exit(0);
    }

    write_pid_in_file(conf,cpid);

    if (cpid > 0)
    {
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        exit(0);
    }
   
    serv(conf->vhosts); //infinite loop 

    return 0;
}

我做这些是为了后续实现守护进程停止功能,请求排查fprintf无法写入PID到文件的原因。


问题排查与修复

核心问题

  1. 父子进程重复写入导致覆盖:fork()后,父进程和子进程会继续执行后续代码,因此两者都会调用write_pid_in_file。父进程写入的是子进程的PID(cpid的值),但子进程中cpid的值为0,且子进程打开文件时使用w+模式会截断文件,最终覆盖父进程写入的有效PID,导致文件中仅保留0。
  2. 子进程传入的PID无效:子进程中cpid为0,并非守护进程实际的PID,正确的守护进程PID应该用getpid()获取。
  3. 错误排查缺失:fopen失败时仅返回错误码,未输出具体错误信息,无法排查文件权限、路径不存在等问题。

修复方案

方案1:仅让父进程写入PID文件

父进程中cpid的值就是子进程(守护进程)的PID,只需将写入逻辑移到父进程分支中,避免子进程重复写入:

int daemon_start(struct config *conf)
{
    pid_t cpid;

    cpid = fork();

    if (cpid == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (cpid > 0)
    {
        // 父进程写入守护进程PID
        int ret = write_pid_in_file(conf, cpid);
        if (ret != 0) {
            perror("failed to write PID file");
            exit(EXIT_FAILURE);
        }
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        exit(EXIT_SUCCESS);
    }
   
    serv(conf->vhosts); // 守护进程进入服务循环
    return 0;
}

方案2:让子进程写入自身PID(更适合复杂守护进程场景)

如果后续需要二次fork(标准守护进程创建流程),父进程退出后子进程的PID才是最终守护进程PID,此时让子进程调用getpid()获取自身PID写入:

int daemon_start(struct config *conf)
{
    pid_t cpid;

    cpid = fork();

    if (cpid == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (cpid > 0)
    {
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        exit(EXIT_SUCCESS);
    }
    // 子进程写入自身PID
    int ret = write_pid_in_file(conf, getpid());
    if (ret != 0) {
        perror("failed to write PID file");
        exit(EXIT_FAILURE);
    }
    serv(conf->vhosts); // 守护进程进入服务循环
    return 0;
}

优化写入函数

增加错误输出,简化文件打开模式(仅写入即可),确保缓冲区刷新:

int write_pid_in_file(struct config *conf, pid_t pid)
{
    // conf->global->pid_file = /tmp/HTTPd.pid
    FILE *file_opened = fopen(conf->global->pid_file, "w"); // 仅写入模式
    if (!file_opened)
    {
        perror("failed to open PID file");
        return 2;
    }
    printf("%d\n", pid); // 调试用输出
    if (fprintf(file_opened, "%d\n", pid) < 0) {
        perror("failed to write PID to file");
        fclose(file_opened);
        return 3;
    }
    fflush(file_opened); // 显式刷新缓冲区,确保写入完成
    fclose(file_opened);
    return 0;
}

额外注意事项

  • 确保PID文件所在目录存在(如/tmp默认存在,自定义路径需提前创建)
  • 运行程序的用户需拥有该路径的写入权限
  • 生产环境中可增加文件锁或存在性检查,避免多个进程同时写入PID文件

内容的提问来源于stack exchange,提问作者Paul Hartmann

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 02:10:30