Python应用程序由于子进程而变得无响应
我用Flask编写了一个Python应用程序,它提供了一个简单的网站,我可以用它在我的Raspberry Pi(微型计算机)上开始播放流式视频。基本上,该应用程序允许be将我的手机或平板电脑用作遥控器 我在Mac OS上测试了这个应用程序,它运行得很好。在将其部署到Raspberry Pi(安装了Debian的Raspbian变体)之后,它可以很好地为网站服务,并且开始播放也可以按预期工作。但是,停止播放失败 相关代码位于此处: 子流程的启动方式如下:Python应用程序由于子进程而变得无响应,python,linux,raspberry-pi,Python,Linux,Raspberry Pi,我用Flask编写了一个Python应用程序,它提供了一个简单的网站,我可以用它在我的Raspberry Pi(微型计算机)上开始播放流式视频。基本上,该应用程序允许be将我的手机或平板电脑用作遥控器 我在Mac OS上测试了这个应用程序,它运行得很好。在将其部署到Raspberry Pi(安装了Debian的Raspbian变体)之后,它可以很好地为网站服务,并且开始播放也可以按预期工作。但是,停止播放失败 相关代码位于此处: 子流程的启动方式如下: cmd = 'python2.7 mlbp
cmd = 'python2.7 mlbplay.py v=%s j=%s/%s/%s i=t1' % (team, mm, dd, yy)
player = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=sys.argv[1])
rmtpdump <some_options_and_url> | mplayer -
这个很好用
子流程应在此之后停止:
player.send_signal(signal.SIGINT)
player.communicate()
这在Mac OS上确实有效,但在Raspberry Pi上不起作用:应用程序将挂起,直到子进程(以cmd
启动)自行完成。子流程似乎未发送或未接收SIGINT
有什么想法吗
(我也在这里发布了这个问题:因为我不知道这是操作系统问题还是Python/Flask相关问题。) 更新: 尝试使用下面Jan Vlcinsky建议的
player.communicate()
(最终看到警告后)没有帮助
我正在考虑使用Jan Vlcinsky提出的解决方案,但如果Flask甚至没有收到请求,我认为这不会收到问题
更新2:
昨天晚上,我很幸运地遇到了这样一种情况:我能够准确地指出问题所在。用相关代码更新了问题
我觉得Jan Vlcinsky的解决方案只会将问题转移到另一个应用程序,这将保持Flask应用程序的响应性,但会让新应用程序挂起
更新3:
我编辑了问题的原始部分,以删除我现在知道不相关的内容
更新4:在@shavenwarthog的评论之后,以下信息可能非常相关:
在Mac上,mlbplay.py启动如下内容:
cmd = 'python2.7 mlbplay.py v=%s j=%s/%s/%s i=t1' % (team, mm, dd, yy)
player = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=sys.argv[1])
rmtpdump <some_options_and_url> | mplayer -
我现在猜测,最后一行启动了一个新的进程组,它没有被SIGINT
信号终止,从而使我的应用程序挂起。如果是这样,我应该以某种方式获取该组的进程组ID,以便能够正确终止它。有人能证实这一点吗
更新5:omxplayer确实处理SIGINT
:
更新6:事实证明,我的SIGINT在命令链的某个地方以某种方式转换为SIGTERM。omxplayer没有正确处理SIGTERM,这似乎就是问题所在。我通过实现一个shell脚本来解决这个问题,该脚本管理信号并将它们转换为适当的omxplayer命令(类似于Jan建议的蹩脚版本)
解决方案:问题出在播放器中。发送信号()
。该信号在命令链中处理不当,导致父应用程序挂起。解决方案是为不能很好地处理信号的命令实现包装器
另外:使用
Popen(cmd.split())
而不是使用shell=True
。这在发送信号时效果更好 问题在以下代码段中标记:
@app.route('/watch/<year>/<month>/<day>/<home>/<away>/')
def watch(year, month, day, home, away):
global session
global watching
global player
# Select video stream
fav = config.get('favorite')
if fav:
fav = fav[0] # TODO: handle multiple favorites
if fav in (home, away):
# Favorite team is playing
team = fav
else:
# Use stream of home team
team = home
else:
# Use stream of home team
team = home
# End session
session = None
# Start mlbplay
mm = '%02i' % int(month)
dd = '%02i' % int(day)
yy = str(year)[-2:]
cmd = 'python2.7 mlbplay.py v=%s j=%s/%s/%s' % (team, mm, dd, yy)
# problem is here ----->
player = subprocess.Popen(cmd, shell=True, cwd=sys.argv[1])
# < ------problem is here
# Render template
game = {}
game['away_code'] = away
game['away_name'] = TEAMCODES[away][1]
game['home_code'] = home
game['home_name'] = TEAMCODES[home][1]
watching = game
return flask.render_template('watching.html', game=game)
@app.route('/watch)/////
优点是,您可以非常轻松地创建其他前端(如命令行前端,甚至远程前端)。还有一个难题:proc.terminate()
vssend\u signal
下面的代码派生一个“播放器”(在本例中只是一个带有sleep
的shell),然后打印其进程信息。它等待片刻,terminate
s播放器,然后验证进程是否已停止,是否已停止
感谢@Jan Vlcinsky在代码中添加了proc.communicate()
(我正在运行Linux Mint LMDE,这是Debian的另一个变体。)
来源
输出
你能分享一些代码吗?我会对你的Flask代码感兴趣(理想情况下是展示问题的完整工作示例,但至少是部分代码,它确实是subprocess.Popen
)。完整的代码在这里托管:子流程在第142行启动,在第1630行停止。我同意将控制器从接口中分离是一个好主意(尽管对于这个小项目来说似乎有点过分)。尽管如此:a)为什么调用其他流程的子流程会出现问题?(顺便说一句,它确实启动了一系列其他流程)。b) 如果std*
缓冲区被填满是真的(我猜这些缓冲区在Raspberry Pi上可能比在Mac上小),为什么它还会阻止Flask应用程序?这不是违背了子进程的全部目的吗?我更愿意向进程发送SIGINT
,因为这个信号被显式地处理,作为一种优雅地退出我试图运行的程序的方式。发送SIGTERM
对我来说似乎有点太严格了。@ludo我明白你的意思了。请注意,SIGTERM
允许优雅退出;这是不可伪装的
SIGKILL。我期待着解决方案!不幸的是,mlbplay不是我的程序,所以我不能添加SIGTERM
处理程序。此外,如果SIGINT
没有到达,为什么SIGTERM
?SIGINT/SIGTERM
应该相同;他们都是可以伪装的。如果发送SIGTERM,会发生什么情况?它的行为是否与SIGINT不同?如果您使用的是以下mlbplay.py,那么它没有任何信号处理程序。Python进程可能正在接收信号,但子进程不会让它死掉。链接指向一个旧版本;这是最新的:在第384行,它捕捉到键盘中断以停止播放。“但是子进程不会让它死亡。”->这可能是一个好的观点。在Mac OS上,我使用的是另一种方式
* player started, PID 20393
Process Details:
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
0 S johnm 20393 20391 0 80 0 - 1110 wait 17:30 pts/4 0:00 /bin/sh -c /bin/sleep 123
*killing player
Process Details:
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD