守护进程和僵尸进程

守护进程和僵尸进程

这是操作系统中两个完全不同的概念,一个是有意设计的特殊进程类型 ,另一个是进程结束后的临时状态

1. 核心区别速览

特征 守护进程 僵尸进程
定义 后台运行的进程,脱离终端 已结束但未被父进程回收的进程
状态 正在运行 已死亡(只保留进程描述符)
目的 提供服务(如 httpd、sshd) 是错误状态,不是设计目标
资源 占用内存、CPU 只占用进程表条目
危害 正常服务 过多会耗尽进程ID
如何产生 主动设计(fork、setsid等) 父进程未调用 wait()
如何解决 正常服务 父进程调用 wait() 或杀死父进程

2. 守护进程(Daemon)

什么是守护进程

守护进程是运行在后台、不与终端交互、持续提供服务的进程

python 复制代码
# 生活中的类比
守护进程 = 物业公司
- 一直在后台运行
- 不直接与住户交互
- 提供电力、清洁、安保等服务

守护进程的特征

  • 没有控制终端(TTY)
  • 通常以 d 结尾(如 sshdhttpdmysqld
  • 父进程通常是 init(PID=1)
  • 独立于用户登录/注销

Python 创建守护进程

python 复制代码
import os
import sys
import time
import signal

def create_daemon():
    """创建守护进程的标准步骤"""
    
    # 1. 第一次 fork(脱离父进程)
    try:
        pid = os.fork()
        if pid > 0:
            sys.exit(0)  # 父进程退出
    except OSError as e:
        print(f"第一次 fork 失败: {e}")
        sys.exit(1)
    
    # 2. 修改工作目录为根目录
    os.chdir("/")
    
    # 3. 创建新的会话,成为会话组长
    os.setsid()
    
    # 4. 第二次 fork(确保不是会话组长,防止获取终端)
    try:
        pid = os.fork()
        if pid > 0:
            sys.exit(0)
    except OSError as e:
        print(f"第二次 fork 失败: {e}")
        sys.exit(1)
    
    # 5. 重设文件权限掩码
    os.umask(0)
    
    # 6. 关闭所有文件描述符
    for fd in range(3, 1024):
        try:
            os.close(fd)
        except OSError:
            pass
    
    # 7. 重定向标准输入、输出、错误到 /dev/null
    sys.stdin = open('/dev/null', 'r')
    sys.stdout = open('/dev/null', 'w')
    sys.stderr = open('/dev/null', 'w')
    
    # 8. 设置信号处理
    signal.signal(signal.SIGTERM, lambda sig, frame: sys.exit(0))

# 使用 Python 的 python-daemon 库(推荐)
try:
    import daemon
    import daemon.pidfile
    
    def my_service():
        while True:
            # 守护进程的工作
            with open('/tmp/daemon.log', 'a') as f:
                f.write(f"守护进程运行中... {time.time()}\n")
            time.sleep(5)
    
    # 创建守护进程
    with daemon.DaemonContext(
        pidfile=daemon.pidfile.PIDLockFile('/tmp/mydaemon.pid'),
        working_directory='/',
        umask=0o077,
    ):
        my_service()
        
except ImportError:
    print("请安装 python-daemon: pip install python-daemon")

简单示例:自定义守护进程

python 复制代码
import os
import sys
import time

def simple_daemon():
    """简化的守护进程示例"""
    
    # fork 子进程
    pid = os.fork()
    if pid > 0:
        print(f"主进程退出,PID={pid}")
        sys.exit(0)
    
    # 子进程成为守护进程
    os.setsid()
    os.chdir('/')
    
    # 记录日志
    with open('/tmp/mydaemon.log', 'w') as log:
        while True:
            log.write(f"守护进程 PID={os.getpid()} 运行中...\n")
            log.flush()
            time.sleep(10)

if __name__ == "__main__":
    simple_daemon()

常见的守护进程示例

python 复制代码
import multiprocessing
import os
import time
import signal

def daemon_service():
    """模拟一个守护服务"""
    pid_file = '/tmp/myservice.pid'
    
    # 检查是否已有实例在运行
    if os.path.exists(pid_file):
        with open(pid_file, 'r') as f:
            old_pid = int(f.read())
            try:
                os.kill(old_pid, 0)
                print(f"服务已在运行,PID={old_pid}")
                return
            except OSError:
                pass
    
    # 写入 PID 文件
    with open(pid_file, 'w') as f:
        f.write(str(os.getpid()))
    
    try:
        while True:
            # 守护进程的主要工作
            with open('/tmp/service.log', 'a') as log:
                log.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 服务运行中\n")
            time.sleep(5)
    except KeyboardInterrupt:
        os.unlink(pid_file)
        print("守护进程停止")

if __name__ == "__main__":
    # 设置为守护进程
    p = multiprocessing.Process(target=daemon_service)
    p.daemon = True  # 守护进程标志
    p.start()
    print(f"守护进程已启动,PID={p.pid}")
    
    # 主进程继续其他工作
    time.sleep(30)

3. 僵尸进程(Zombie Process)

什么是僵尸进程

僵尸进程是已经结束执行,但其进程描述符仍然保留在进程表中的进程

python 复制代码
# 生活中的类比
僵尸进程 = 已死亡但未办理销户的人
- 人已经死了(不再工作)
- 但户口本上还有记录(占用名额)

僵尸进程的产生原因

python 复制代码
import multiprocessing
import os
import time

def child_process():
    """子进程会正常结束"""
    print(f"子进程 {os.getpid()} 开始工作")
    time.sleep(2)
    print(f"子进程 {os.getpid()} 结束")
    # 进程结束,但父进程没有调用 wait()

if __name__ == "__main__":
    print(f"父进程 PID: {os.getpid()}")
    
    # 创建子进程
    p = multiprocessing.Process(target=child_process)
    p.start()
    
    # 父进程不调用 p.join() 或 os.wait()
    # 子进程结束后会变成僵尸进程
    
    print("父进程继续运行...")
    time.sleep(10)  # 父进程没有回收子进程
    
    # 查看僵尸进程: ps aux | grep defunct

查看僵尸进程

bash 复制代码
# Linux/Unix 命令
ps aux | grep defunct
ps -e -o pid,ppid,stat,cmd | grep Z

# 输出示例
# 12345  12344  Z+   [python] <defunct>

避免僵尸进程的方法

方法1:使用 join() 回收
python 复制代码
import multiprocessing
import time

def worker():
    time.sleep(1)

if __name__ == "__main__":
    p = multiprocessing.Process(target=worker)
    p.start()
    p.join()  # 等待子进程结束并回收
方法2:使用信号处理自动回收
python 复制代码
import signal
import os
import time

# 设置 SIGCHLD 信号处理函数
def handle_sigchld(signum, frame):
    """当子进程结束时自动回收"""
    try:
        while True:
            # 非阻塞方式回收所有子进程
            pid, status = os.waitpid(-1, os.WNOHANG)
            if pid == 0:
                break
            print(f"回收子进程 {pid}")
    except ChildProcessError:
        pass

signal.signal(signal.SIGCHLD, handle_sigchld)

def worker():
    print(f"子进程 {os.getpid()} 工作")
    time.sleep(1)

if __name__ == "__main__":
    for i in range(5):
        pid = os.fork()
        if pid == 0:
            worker()
            exit(0)
    
    time.sleep(2)  # 等待子进程结束并自动回收
方法3:使用进程池自动管理
python 复制代码
from multiprocessing import Pool
import time

def worker(x):
    time.sleep(1)
    return x * 2

if __name__ == "__main__":
    # 进程池会自动回收子进程
    with Pool(processes=4) as pool:
        results = pool.map(worker, range(10))
    # 退出 with 块时自动清理
方法4:双 fork 技术(隔离僵尸)
python 复制代码
import os
import time
import sys

def create_orphan():
    """创建孤儿进程,由 init 收养"""
    pid = os.fork()
    if pid > 0:
        # 父进程立即退出,子进程成为孤儿
        sys.exit(0)
    
    # 子进程成为孤儿,被 init(PID=1) 收养
    os.setsid()
    pid = os.fork()
    if pid > 0:
        sys.exit(0)
    
    # 这个进程会被 init 自动回收
    while True:
        time.sleep(1)
        print(f"孤儿进程运行中,PID={os.getpid()}")

if __name__ == "__main__":
    create_orphan()

模拟僵尸进程的完整示例

python 复制代码
import os
import time

def create_zombie():
    """创建僵尸进程(演示用)"""
    pid = os.fork()
    
    if pid == 0:
        # 子进程
        print(f"子进程 {os.getpid()} 即将结束")
        exit(0)  # 子进程结束
    else:
        # 父进程
        print(f"父进程 {os.getpid()} 创建了子进程 {pid}")
        print("父进程不回收子进程,子进程将成为僵尸")
        print(f"运行 'ps aux | grep {pid}' 查看僵尸进程")
        
        # 保持父进程运行
        time.sleep(60)
        
        # 最后回收
        os.waitpid(pid, 0)
        print("回收僵尸进程")

if __name__ == "__main__":
    create_zombie()

4. 守护进程 vs 僵尸进程对比图

复制代码
守护进程(正常运行的进程)
┌─────────────────────────────────────┐
│  状态: 运行中 (Running/Sleeping)     │
│  内存: 占用正常内存                   │
│  CPU: 占用CPU资源                    │
│  文件: 打开文件描述符                 │
│  目的: 提供服务                      │
└─────────────────────────────────────┘
         ↓ 正常服务
    持续运行直到停止

僵尸进程(已死但未清理)
┌─────────────────────────────────────┐
│  状态: 僵尸 (Zombie/Defunct)         │
│  内存: 只保留进程表条目               │
│  CPU: 不占用CPU                      │
│  文件: 所有资源已释放                 │
│  原因: 父进程未调用 wait()            │
└─────────────────────────────────────┘
         ↓ 解决方法
    父进程 wait() 或 杀死父进程

5. 实用命令和调试

bash 复制代码
# 查看守护进程
ps -e -o pid,ppid,stat,cmd | grep -E '^ *[0-9]+.*[d]$'
ps aux | grep -E '/(usr|bin)/.*d$'

# 查看僵尸进程
ps aux | grep defunct
ps -e -o pid,ppid,stat,cmd | grep Z

# 查看进程树(显示父子关系)
pstree -p

# 查看所有进程状态
ps -e -o pid,ppid,stat,comm

# 统计僵尸进程数量
ps -e -o stat | grep Z | wc -l

6. Python 中管理守护和僵尸的完整示例

python 复制代码
import os
import signal
import time
import multiprocessing

class ProcessManager:
    """进程管理器"""
    
    def __init__(self):
        self.processes = []
        # 设置信号处理,自动回收僵尸进程
        signal.signal(signal.SIGCHLD, self._handle_sigchld)
    
    def _handle_sigchld(self, signum, frame):
        """处理子进程结束信号"""
        try:
            while True:
                pid, status = os.waitpid(-1, os.WNOHANG)
                if pid == 0:
                    break
                print(f"自动回收子进程 {pid}")
        except ChildProcessError:
            pass
    
    def create_daemon(self, target, *args, **kwargs):
        """创建守护进程"""
        p = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
        p.daemon = True
        p.start()
        self.processes.append(p)
        return p
    
    def create_normal(self, target, *args, **kwargs):
        """创建普通进程(会被回收)"""
        p = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
        p.start()
        self.processes.append(p)
        return p
    
    def wait_all(self):
        """等待所有进程结束"""
        for p in self.processes:
            p.join()
    
    def cleanup(self):
        """清理所有进程"""
        for p in self.processes:
            if p.is_alive():
                p.terminate()
                p.join()

def worker(name, duration):
    """工作进程"""
    print(f"进程 {name} (PID={os.getpid()}) 开始工作 {duration}秒")
    time.sleep(duration)
    print(f"进程 {name} 结束")

if __name__ == "__main__":
    pm = ProcessManager()
    
    # 创建守护进程
    daemon = pm.create_daemon(worker, "守护进程", 30)
    print(f"守护进程 PID: {daemon.pid}")
    
    # 创建普通进程
    normal = pm.create_normal(worker, "普通进程", 3)
    print(f"普通进程 PID: {normal.pid}")
    
    # 等待普通进程结束
    normal.join()
    
    # 清理守护进程
    time.sleep(2)
    pm.cleanup()
    
    print("主进程结束")

总结

  • 守护进程:有意设计的后台服务进程,持续运行提供功能
  • 僵尸进程:已结束但未被回收的错误状态,应避免
  • 守护进程通过特殊技术(fork、setsid)创建,脱离终端
  • 僵尸进程通过正确调用 wait()/join() 来避免
  • 在 Python 中,multiprocessing 模块会自动处理大部分回收工作
  • 使用进程池可以避免手动管理进程生命周期
相关推荐
falldeep7 分钟前
五分钟了解OpenClaw底层架构
人工智能·算法·机器学习·架构
FserSuN9 分钟前
Machine Learning Specialization - Week 1, 1-8学习总结
人工智能·学习·机器学习
weixin_4462608513 分钟前
模型能力深度对决:GPT-4o、Claude 3.5和DeepSeek V系列模型的横向评测与未来趋势洞察
人工智能·算法·机器学习
Mr数据杨39 分钟前
泰坦尼克乘客生存预测与风险决策建模
机器学习·数据分析·kaggle
Godspeed Zhao2 小时前
具身智能中的传感器技术41——事件相机1
人工智能·科技·机器学习·具身智能·事件相机
AI科技星2 小时前
全域数学视角下N维广义数系的推广与本源恒等式构建【乖乖数学】
人工智能·机器学习·数学建模·数据挖掘
程序媛小鱼2 小时前
吴恩达 Agent Skills 学习笔记
机器学习
MediaTea2 小时前
人工智能通识课:Scikit-learn 机器学习工具库
人工智能·python·机器学习·scikit-learn
郝学胜-神的一滴2 小时前
二分类任务核心:BCE 损失函数从原理到 PyTorch 实战
人工智能·pytorch·python·算法·机器学习·分类·数据挖掘
一水鉴天2 小时前
同构异质三表总装体系确立与入表机制闭环验证 20260502(腾讯元宝)
人工智能·算法·机器学习