父进程如何获取通过双fork创建的守护进程PID?
获取双fork守护进程的孙进程PID方案
针对双fork创建守护进程后无法在原父进程获取最终孙进程PID的问题,以下是两种安全可靠的实现方案:
方案一:通过文件系统存储PID
这是最通用且持久化的方案,适合大多数场景,即使原父进程退出后,后续也能通过文件读取守护进程PID。
实现思路
- 原父进程启动后,发起第一次fork;
- 子进程1创建新会话后发起第二次fork;
- 孙进程(最终守护进程)将自身PID写入指定文件,并设置严格的文件权限防止篡改;
- 原父进程通过轮询等待PID文件生成,读取后即可获取守护进程PID。
代码示例
import os import sys import time def create_daemon(pid_file_path): try: child1_pid = os.fork() if child1_pid > 0: # 原父进程:轮询等待PID文件生成 max_wait_seconds = 5 wait_count = 0 daemon_pid = None while wait_count < max_wait_seconds: if os.path.exists(pid_file_path): with open(pid_file_path, 'r') as f: daemon_pid = int(f.read().strip()) print(f"成功获取守护进程PID: {daemon_pid}") break time.sleep(1) wait_count += 1 if not daemon_pid: print("超时未获取到守护进程PID", file=sys.stderr) return except OSError as e: print(f"第一次fork失败: {e}", file=sys.stderr) sys.exit(1) # 子进程1:创建新会话脱离终端 os.setsid() try: child2_pid = os.fork() if child2_pid > 0: # 子进程1退出,孙进程成为孤儿进程被init接管 sys.exit(0) except OSError as e: print(f"第二次fork失败: {e}", file=sys.stderr) sys.exit(1) # 孙进程(守护进程):写入PID到文件并设置权限 with open(pid_file_path, 'w') as f: f.write(str(os.getpid())) os.chmod(pid_file_path, 0o600) # 仅当前用户可读可写,避免篡改 # 守护进程核心逻辑示例 while True: time.sleep(3600) if __name__ == "__main__": create_daemon("/var/run/my_daemon.pid")
方案二:通过匿名管道传递PID
适合需要实时获取PID且不想生成额外文件的场景,利用进程间管道直接传递数据。
实现思路
- 原父进程创建匿名管道;
- 第一次fork后,子进程1继承管道并关闭读端;
- 第二次fork后,孙进程继承管道并将自身PID写入管道写端;
- 原父进程从管道读端读取PID,完成后关闭管道。
代码示例
import os import sys import time def create_daemon(): # 创建匿名管道:r为读端,w为写端 read_fd, write_fd = os.pipe() try: child1_pid = os.fork() if child1_pid > 0: # 原父进程:关闭写端,读取PID os.close(write_fd) with os.fdopen(read_fd, 'r') as f: daemon_pid = int(f.read().strip()) print(f"成功获取守护进程PID: {daemon_pid}") return except OSError as e: print(f"第一次fork失败: {e}", file=sys.stderr) sys.exit(1) # 子进程1:关闭读端,创建新会话 os.close(read_fd) os.setsid() try: child2_pid = os.fork() if child2_pid > 0: # 子进程1退出,关闭写端释放资源 os.close(write_fd) sys.exit(0) except OSError as e: print(f"第二次fork失败: {e}", file=sys.stderr) sys.exit(1) # 孙进程(守护进程):写入PID到管道,关闭写端 with os.fdopen(write_fd, 'w') as f: f.write(str(os.getpid())) os.close(write_fd) # 守护进程核心逻辑示例 while True: time.sleep(3600) if __name__ == "__main__": create_daemon()
方案对比与安全注意事项
- 文件存储方案:优点是持久化,后续可随时读取PID;缺点是需要处理文件权限、存在性判断,轮询会有轻微延迟。
- 管道传递方案:优点是实时性强,无额外文件生成;缺点是原父进程需阻塞等待读取,无法直接返回。
- 安全要点:文件存储时必须设置
0o600权限;管道传递时要确保所有未使用的管道描述符被关闭,避免资源泄漏;两种方案都需添加超时处理,防止原父进程无限等待。
内容的提问来源于stack exchange,提问作者CalebK
相关产品推荐
相关产品推荐

