Python 2.7.9子进程无法终止求助:类屏保功能实现遇阻
解决屏保子进程无法终止的问题
看起来你遇到了类似Windows屏保的逻辑实现难题——启动子进程后没法用常规方法终止,还碰到了进程ID相关的错误。我来帮你一步步排查和解决:
问题分析
p.kill()/p.terminate()无效:大概率是因为strandtest.py启动时创建了自己的子进程,或者你用shell=True启动脚本(此时p.pid是shell进程的ID,而非strandtest.py本身),只终止父进程不会连带终止子进程。module' object has no attribute 'pid'错误:这个错误说明你调用os.getpgid()时传入的不是Popen实例的pid,而是误引用了某个模块(比如subprocess.pid),而不是你创建的进程对象p的pid属性。
解决方案
核心思路是让strandtest.py在独立的进程组中运行,这样我们可以一次性杀掉整个进程组,确保所有相关进程都被终止。同时修正变量引用错误,正确跟踪进程实例。
步骤1:正确启动子进程
启动strandtest.py时,添加start_new_session=True参数,让它成为新进程组的组长:
# 替换你原来的启动代码 screensaver_process = Popen(["python", "strandtest.py"], start_new_session=True)
这个参数会让子进程脱离当前进程组,自己创建一个新组,后续我们可以通过进程组ID杀掉所有关联进程。
步骤2:正确终止进程组
当检测到遥控器输入时,用进程组ID终止整个进程组,而不是单个进程:
import os import signal def stop_screensaver(): global screensaver_process if screensaver_process is not None: try: # 获取子进程的进程组ID pgid = os.getpgid(screensaver_process.pid) # 发送SIGTERM信号终止整个进程组 os.killpg(pgid, signal.SIGTERM) # 等待进程结束,避免僵尸进程 screensaver_process.wait() except ProcessLookupError: # 如果进程已经自行结束,忽略错误 pass screensaver_process = None
步骤3:完善主逻辑(类似屏保的触发与重置)
添加计时器逻辑,检测用户输入并重置计时,5秒无输入则启动屏保,有输入则终止屏保:
import lirc import time # 初始化变量 screen_saver_running = False screensaver_process = None last_input_time = time.time() # 初始化lirc(保留你原来的代码) sockid = lirc.init("lifetec", blocking=False) # 主循环 while True: # 检测遥控器输入 code = lirc.nextcode() if code: print(f"收到遥控器指令: {code}") last_input_time = time.time() # 如果屏保正在运行,立即终止 if screen_saver_running: stop_screensaver() screen_saver_running = False # 这里添加你的菜单处理逻辑... # 5秒无输入且未启动屏保时,启动屏保 if not screen_saver_running and (time.time() - last_input_time) > 5: start_screensaver() screen_saver_running = True # 小延迟降低CPU占用 time.sleep(0.1)
关键注意事项
- 确保你没有误写变量名:比如不要把
p.pid写成subprocess.pid(这就是你碰到"module has no attribute pid"的原因)。 - 避免用
shell=True启动脚本:如果必须用,你需要额外处理子进程的PID,但用start_new_session=True+进程组的方式更可靠。 - 调用
wait():终止进程后调用screensaver_process.wait()可以避免僵尸进程残留。
完整修改后的代码片段
整合以上逻辑,你的代码大概会变成这样(保留你原来的LED初始化等代码):
import lirc import time import os import signal from subprocess import PIPE, Popen from neopixel import * # 全局变量 Volume = 60 mute = False screen_saver_running = False screensaver_process = None last_input_time = time.time() # LED配置(保留你的设置) LEDS = 100 PIN = 18 BRIGHTNESS = 255 R = 255 G = 255 B = 255 LED_CHANNEL = 0 LED_STRIP = ws.WS2811_STRIP_GRB # 初始化硬件 sockid = lirc.init("lifetec", blocking=False) strip = Adafruit_NeoPixel(LEDS, PIN, 800000, 10, False, BRIGHTNESS, LED_CHANNEL, LED_STRIP) strip.begin() def start_screensaver(): global screensaver_process print("启动屏保...") screensaver_process = Popen(["python", "strandtest.py"], start_new_session=True) def stop_screensaver(): global screensaver_process if screensaver_process is not None: try: pgid = os.getpgid(screensaver_process.pid) os.killpg(pgid, signal.SIGTERM) screensaver_process.wait() print("终止屏保") except ProcessLookupError: print("屏保进程已结束") screensaver_process = None # 主循环 while True: # 检测遥控器输入 code = lirc.nextcode() if code: last_input_time = time.time() if screen_saver_running: stop_screensaver() screen_saver_running = False # 处理菜单逻辑:比如根据code执行不同操作... # 触发屏保逻辑 if not screen_saver_running and (time.time() - last_input_time) > 5: start_screensaver() screen_saver_running = True time.sleep(0.1)
内容的提问来源于stack exchange,提问作者user1878635
相关产品推荐
相关产品推荐

