通过两个子类扩展Daemon类无法正常工作的技术咨询
解决Daemon基类子类化多守护进程的问题
我明白你在尝试通过两个子类扩展这个Daemon基类时遇到了问题——这类守护进程的继承问题通常出在进程隔离或者run()方法重写不规范上,咱们一步步来解决。
首先,先把你提供的Daemon基类补全完整(双叉机制的实现需要完整的环境脱离和文件描述符重定向逻辑):
import os import sys import time from signal import SIGTERM class Daemon: """通用守护进程类。使用方法:继承该守护进程类并重写run()方法。""" def __init__(self, pidfile, outfile='/tmp/daemon_out', errfile='/tmp/daemon_log'): self.pidfile = pidfile self.outfile = outfile self.errfile = errfile def daemonize(self): """守护进程化类。采用UNIX双叉机制。""" try: pid = os.fork() if pid > 0: # 父进程退出,脱离终端控制 sys.exit(0) except OSError as e: sys.stderr.write(f"第一次fork失败: {e.errno} ({e.strerror})\n") sys.exit(1) # 脱离父进程的会话和环境 os.chdir("/") os.setsid() os.umask(0) # 第二次fork,确保进程不会成为会话首进程,避免再次打开终端 try: pid = os.fork() if pid > 0: sys.exit(0) except OSError as e: sys.stderr.write(f"第二次fork失败: {e.errno} ({e.strerror})\n") sys.exit(1) # 重定向标准输入、输出、错误到指定文件或/dev/null sys.stdout.flush() sys.stderr.flush() si = open(os.devnull, 'r') so = open(self.outfile, 'a+') se = open(self.errfile, 'a+') os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) # 写入PID文件,用于后续启停控制 with open(self.pidfile, 'w+') as f: f.write(f"{os.getpid()}\n") def start(self): """启动守护进程""" # 检查PID文件是否存在,防止重复启动 try: with open(self.pidfile, 'r') as f: pid = int(f.read().strip()) except IOError: pid = None if pid: message = f"PID文件 {self.pidfile} 已存在,守护进程可能已运行?\n" sys.stderr.write(message) sys.exit(1) # 完成守护进程化并执行核心逻辑 self.daemonize() self.run() def stop(self): """停止守护进程""" # 从PID文件读取进程ID try: with open(self.pidfile, 'r') as f: pid = int(f.read().strip()) except IOError: pid = None if not pid: message = f"PID文件 {self.pidfile} 不存在,守护进程可能未运行?\n" sys.stderr.write(message) return # 发送终止信号并等待进程退出 try: while True: os.kill(pid, SIGTERM) time.sleep(0.1) except OSError as e: if e.errno == 3: # 进程已不存在 if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print(str(e)) sys.exit(1) def restart(self): """重启守护进程""" self.stop() self.start() def run(self): """需要被子类重写的核心方法,子类在这里实现具体业务逻辑""" raise NotImplementedError("子类必须重写run()方法")
接下来,创建两个独立的子类,关键是给每个子类分配独立的PID文件和日志文件,确保进程之间完全隔离:
# 第一个子类:日志收集守护进程 class LogCollectorDaemon(Daemon): def __init__(self): # 指定专属的PID文件和日志路径,避免和其他守护进程冲突 super().__init__( pidfile='/var/run/log_collector.pid', outfile='/var/log/log_collector_out.log', errfile='/var/log/log_collector_err.log' ) def run(self): """模拟日志收集逻辑:每秒写入一条收集记录""" while True: with open(self.outfile, 'a') as f: f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 正在收集系统日志...\n") time.sleep(1) # 第二个子类:定时任务守护进程 class ScheduledTaskDaemon(Daemon): def __init__(self): super().__init__( pidfile='/var/run/scheduled_task.pid', outfile='/var/log/scheduled_task_out.log', errfile='/var/log/scheduled_task_err.log' ) def run(self): """模拟定时任务:每5秒执行一次任务""" while True: with open(self.outfile, 'a') as f: f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 执行定时任务...\n") time.sleep(5)
最后,在控制器文件中实现对这两个守护进程的启停控制:
# controller.py import sys import time from your_daemon_module import LogCollectorDaemon, ScheduledTaskDaemon # 替换为你的Daemon类所在模块名 def main(): if len(sys.argv) != 3: print("用法: python controller.py <daemon_name> <start|stop|restart>") print("可选daemon_name: log_collector, scheduled_task") sys.exit(1) daemon_name = sys.argv[1] action = sys.argv[2] # 根据参数选择对应的守护进程实例 daemon = None if daemon_name == 'log_collector': daemon = LogCollectorDaemon() elif daemon_name == 'scheduled_task': daemon = ScheduledTaskDaemon() else: print(f"未知的守护进程名称: {daemon_name}") sys.exit(1) # 执行对应的操作 if action == 'start': daemon.start() elif action == 'stop': daemon.stop() elif action == 'restart': daemon.restart() else: print(f"未知操作: {action},可选值: start|stop|restart") sys.exit(1) if __name__ == "__main__": main()
常见问题排查
- PID文件冲突:每个子类必须使用独立的PID文件,否则会出现"进程已运行"的错误。
- 权限不足:如果把PID文件放在
/var/run或日志放在/var/log,需要用root权限运行脚本(比如sudo python controller.py log_collector start)。 - run()方法未重写:如果子类没有实现
run()方法,启动时会抛出NotImplementedError,一定要记得重写这个方法。 - 日志混乱:不要让多个守护进程共享同一个输出/错误文件,否则日志会互相覆盖,难以排查问题。
内容的提问来源于stack exchange,提问作者Srivatsa M
相关产品推荐
相关产品推荐

