守护进程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到文件的原因。
问题排查与修复
核心问题
- 父子进程重复写入导致覆盖:
fork()后,父进程和子进程会继续执行后续代码,因此两者都会调用write_pid_in_file。父进程写入的是子进程的PID(cpid的值),但子进程中cpid的值为0,且子进程打开文件时使用w+模式会截断文件,最终覆盖父进程写入的有效PID,导致文件中仅保留0。 - 子进程传入的PID无效:子进程中
cpid为0,并非守护进程实际的PID,正确的守护进程PID应该用getpid()获取。 - 错误排查缺失:
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
相关产品推荐
相关产品推荐

