树莓派Python监听脚本内存耗尽问题求助(TCP/HTTP)
问题描述
在树莓派(RPi)上需要实现一个可被其他设备触发的Python脚本事件,将GPIO输出置高60秒。先后尝试了TCP监听和HTTP GET两种方案,均出现内存耗尽导致系统崩溃的问题,而不运行监听脚本时系统可稳定运行数月。
TCP监听方案代码
def run_tcp_listener(): global PINO_MOB # = 16 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('', 8123)) server_socket.listen(1) while True: GPIO.output(PINO_MOB, GPIO.LOW) client_socket, client_address = server_socket.accept() message = client_socket.recv(16) if message == 'Gong_Familie': GPIO.output(PINO_MOB, GPIO.HIGH) client_socket.close() time.sleep(60)
该任务以独立进程启动:
proc = Process(target=run_tcp_listener) proc.start()
HTTP GET监听方案代码
def run_http_listener(): # Die Adresse und Portnummer, an der der Server lauschen soll server_address = ('', 8124) # Erstellt eine Instanz des HTTP-Servers mit der eigenen Handler-Klasse httpd = HTTPServer(server_address, MyHTTPRequestHandler) # Startet den Server und laesst ihn auf eingehende Anfragen warten httpd.serve_forever() # eigene Handler-Klasse, die von BaseHTTPRequestHandler erbt class MyHTTPRequestHandler(BaseHTTPRequestHandler): # wird aufgerufen, wenn eine HTTP GET-Anforderung empfangen wird def do_GET(self): global PINO_MOB # Setzt den HTTP-Statuscode auf 200 (OK) self.send_response(200) # Setzt den Content-Type Header self.send_header('Content-type', 'text/html') self.end_headers() # Aktiviert GPIO (Mobotix) fuer 60s if self.path == '/Gong_Familie': GPIO.output(PINO_MOB, GPIO.HIGH) time.sleep(60) GPIO.output(PINO_MOB, GPIO.LOW)
该任务同样以独立进程启动:
proc = Process(target=run_http_listener) proc.start()
问题现象
两种方案运行不到1小时就会出现内存耗尽,交换文件(swap)开始增长后系统很快卡死,触摸屏无响应,甚至无法通过SSH重新登录。使用环境为Python 2.7、Jessie Lite(kiosk模式),曾尝试升级Python 3.x遇问题后回退。
解决办法
1. 修复TCP方案的资源泄漏
原TCP代码中,客户端连接关闭后可能存在资源回收不及时的情况,添加异常处理确保资源彻底释放:
def run_tcp_listener(): global PINO_MOB # = 16 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(('', 8123)) server_socket.listen(1) while True: GPIO.output(PINO_MOB, GPIO.LOW) client_socket = None try: client_socket, client_address = server_socket.accept() message = client_socket.recv(16) if message == 'Gong_Familie': GPIO.output(PINO_MOB, GPIO.HIGH) finally: if client_socket: client_socket.close() client_socket = None # 强制释放引用 time.sleep(60)
2. 改进HTTP方案的阻塞问题
原HTTP方案中do_GET内的time.sleep(60)会阻塞整个服务器,导致请求排队和资源累积,改用线程处理GPIO延时:
import threading def activate_gpio(): global PINO_MOB GPIO.output(PINO_MOB, GPIO.HIGH) time.sleep(60) GPIO.output(PINO_MOB, GPIO.LOW) class MyHTTPRequestHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() if self.path == '/Gong_Familie': # 启动独立线程处理延时,不阻塞主服务 threading.Thread(target=activate_gpio).start()
3. 强制Python内存回收
在脚本开头添加内存回收配置,帮助Python 2.7及时释放闲置内存:
import gc import threading import time gc.enable() def periodic_gc(): while True: gc.collect() time.sleep(300) # 每5分钟主动回收一次内存 # 在启动监听前启动回收线程 threading.Thread(target=periodic_gc, daemon=True).start()
4. 替换为轻量级shell方案
如果上述方法仍无效,可完全避开Python,用netcat配合shell脚本实现:
创建trigger_gpio.sh脚本:
#!/bin/bash GPIO_PIN=16 # 导出GPIO引脚 echo "$GPIO_PIN" > /sys/class/gpio/export echo "out" > /sys/class/gpio/gpio$GPIO_PIN/direction # 置高引脚 echo "1" > /sys/class/gpio/gpio$GPIO_PIN/value sleep 60 # 置低并释放引脚 echo "0" > /sys/class/gpio/gpio$GPIO_PIN/value echo "$GPIO_PIN" > /sys/class/gpio/unexport
赋予执行权限:
chmod +x trigger_gpio.sh
用netcat监听端口触发脚本:
nc -l -p 8123 -e ./trigger_gpio.sh
该方案资源占用极低,完全避免Python内存问题。
内容的提问来源于stack exchange,提问作者DerLord
相关产品推荐
相关产品推荐

