树莓派PiFmRds切换频率时子进程内存泄漏问题求助
问题描述
我想用树莓派扩展板的左右按键切换广播频率,用的是PiFmRds库。但切换几次频率后,树莓派就内存耗尽,抛出“Could not allocate memory.”错误。想问旧的子进程是不是一直在运行占内存?如果是,该怎么终止旧进程?
附上代码:
import os import board import busio import adafruit_character_lcd.character_lcd_rgb_i2c as character_lcd import time import subprocess import sys import digitalio import threading import signal lcd_columns = 16 lcd_rows = 2 i2c = busio.I2C(board.SCL, board.SDA) lcd = character_lcd.Character_LCD_RGB_I2C(i2c, lcd_columns, lcd_rows) freq = 103.3 while True: try: freq = round(freq, 1) bashCommand = "sudo ./pi_fm_rds -audio sound.wav -pi 0000 -freq " + str(freq) lcd.clear() lcd.message = str(freq) transmit.send_signal(signal.SIGINT) transmit = subprocess.Popen([bashCommand], stdout=subprocess.PIPE, shell=True) print(freq) while lcd.left_button or lcd.right_button == False: if lcd.left_button: freq = freq - 0.1 while lcd.left_button == True: pass break if lcd.right_button: freq = freq + 0.1 while lcd.right_button == True: pass break except ValueError: print(ValueError) exit()
问题根源
你的猜测完全正确——旧的pi_fm_rds子进程根本没被正确终止,一直在后台运行,每次切换频率都会新增一个进程,几次下来就把内存占满了。
代码里的具体问题:
- 第一次循环时
transmit变量未定义就调用send_signal,直接触发异常(虽然可能被后续流程掩盖,但逻辑上完全错误) - 终止旧进程的时机颠倒:你先更新LCD显示,再尝试杀旧进程,最后才启动新进程,而且没有等待旧进程彻底退出就开新的
- 用
shell=True启动进程,额外多了一个shell进程占用资源,且未处理子进程的输出缓冲区,可能导致进程阻塞残留
修复方案
核心思路
每次切换频率前,先彻底终止旧的pi_fm_rds进程,释放资源后再启动新进程。
修复后的代码
import os import board import busio import adafruit_character_lcd.character_lcd_rgb_i2c as character_lcd import time import subprocess import sys import signal lcd_columns = 16 lcd_rows = 2 i2c = busio.I2C(board.SCL, board.SDA) lcd = character_lcd.Character_LCD_RGB_I2C(i2c, lcd_columns, lcd_rows) freq = 103.3 transmit = None # 初始化进程变量,避免首次循环报错 while True: try: freq = round(freq, 1) # 用列表形式传命令,避免shell=True,减少额外进程开销 cmd_args = ["sudo", "./pi_fm_rds", "-audio", "sound.wav", "-pi", "0000", "-freq", str(freq)] lcd.clear() lcd.message = str(freq) # 终止旧进程:先优雅终止,超时则强制杀死 if transmit is not None: try: transmit.send_signal(signal.SIGINT) # 等待3秒让进程优雅退出 transmit.wait(timeout=3) except subprocess.TimeoutExpired: # 超时就强制杀死 transmit.kill() transmit.wait() # 确保进程彻底退出 transmit = None # 启动新的传输进程,重定向输出到/dev/null避免缓冲区阻塞 transmit = subprocess.Popen( cmd_args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL ) print(freq) # 优化按键检测循环,降低CPU占用 while True: if lcd.left_button: freq -= 0.1 # 等待按键释放,防止连续触发 while lcd.left_button: time.sleep(0.05) break if lcd.right_button: freq += 0.1 while lcd.right_button: time.sleep(0.05) break time.sleep(0.05) except ValueError as e: print(e) # 退出前清理进程 if transmit is not None: transmit.kill() transmit.wait() sys.exit() except Exception as e: # 捕获其他异常(比如进程已退出时调用send_signal) print(f"异常: {e}") transmit = None
额外优化说明
- 按键检测循环加入
time.sleep(0.05),大幅降低CPU占用 - 把输出重定向到
DEVNULL,避免子进程因输出缓冲区满而阻塞残留 - 退出程序时主动终止子进程,防止后台残留
内容的提问来源于stack exchange,提问作者Emil12232335
相关产品推荐
相关产品推荐

