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

父进程如何获取通过双fork创建的守护进程PID?

获取双fork守护进程的孙进程PID方案

针对双fork创建守护进程后无法在原父进程获取最终孙进程PID的问题,以下是两种安全可靠的实现方案:

方案一:通过文件系统存储PID

这是最通用且持久化的方案,适合大多数场景,即使原父进程退出后,后续也能通过文件读取守护进程PID。

实现思路

  1. 原父进程启动后,发起第一次fork;
  2. 子进程1创建新会话后发起第二次fork;
  3. 孙进程(最终守护进程)将自身PID写入指定文件,并设置严格的文件权限防止篡改;
  4. 原父进程通过轮询等待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且不想生成额外文件的场景,利用进程间管道直接传递数据。

实现思路

  1. 原父进程创建匿名管道;
  2. 第一次fork后,子进程1继承管道并关闭读端;
  3. 第二次fork后,孙进程继承管道并将自身PID写入管道写端;
  4. 原父进程从管道读端读取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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 15:25:28