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

树莓派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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:30:14