我已经使用Flask编写了一个Python应用程序,它提供了一个简单的网站,我可以使用它开始在Raspberry Pi(微机)上播放stream式video。 本质上,应用程序允许将我的手机或平板电脑用作遥控器。
我在Mac OS上testing了这个应用程序,它工作正常。 将其部署到Raspberry Pi(安装了Debian的Raspbian变体)之后,它可以很好地服务于网站,并且开始播放也可以按预期工作。 但是,停止播放失败。
相关代码托pipe在这里: https : //github.com/lcvisser/mlbviewer-remote/blob/master/remote/mlbviewer-remote.py
subprocess是这样启动的:
cmd = 'python2.7 mlbplay.py v=%sj=%s/%s/%si=t1' % (team, mm, dd, yy) player = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=sys.argv[1])
这工作正常。
这个subprocess应该在这之后停止:
player.send_signal(signal.SIGINT) player.communicate()
这可以在Mac OS上运行,但是在Raspberry Pi上不起作用:应用程序挂起,直到subprocess(由cmd
启动)自行完成。 好像SIGINT
没有发送或没有SIGINT
接收。
有任何想法吗?
(我也发布这个问题也在这里: https : //unix.stackexchange.com/questions/133946/application-becomes-non-responsive-to-requests-on-raspberry-pi,因为我不知道这是否是操作系统的问题,如果它是一个Python /烧瓶相关的问题。)
更新:尝试使用下面的Jan Vlcinskybuild议使用player.communicate()
(并在终于看到这里的警告后)没有帮助。
我正在考虑使用Jan Vlcinsky提出的解决scheme,但如果Flask甚至没有收到请求,我不认为会收到问题。
更新2:昨天晚上,我很幸运地有一个情况,我能够准确地指出这个问题。 用相关的代码更新了这个问题。
我觉得Jan Vlcinsky的解决scheme只是将问题转移到一个不同的应用程序,这将使Flask应用程序保持响应,但会让新应用程序挂起。
更新3:我编辑了问题的原始部分,删除我现在知道不相关的部分。
更新4: @shavenwarthog的评论后,以下信息可能是非常相关的:
在Mac上,mlbplay.py启动如下所示:
rmtpdump <some_options_and_url> | mplayer -
将SIGINT
发送到mlbplay.py时,会终止由此pipe道命令创build的进程组 (如果我理解正确的话)。
在Raspberry Pi上,我使用的是omplayer,但是为了避免更改mlbplay.py(这不是我的)的代码,我制作了一个名为mplayer的脚本,内容如下:
#!/bin/bash MLBTV_PIPE=mlbpipe if [ ! -p $MLBTV_PIPE ] then mkfifo $MLBTV_PIPE fi cat <&0 > $MLBTV_PIPE | omxplayer -o hdmi $MLBTV_PIPE
我现在猜测,这最后一行开始一个新的进程组,这是不是由SIGINT
信号终止,从而使我的应用程序挂起。 如果是这样,我应该以某种方式获得该组的进程组ID以便能够正确地终止它。 有人可以证实这一点?
更新5: omxplayer处理SIGINT
:
https://github.com/popcornmix/omxplayer/blob/master/omxplayer.cpp#L131
更新6:事实certificate,我的SIGINT在命令链的某处转换成SIGTERM。 SIGMEMM不能被omxplayer正确处理,这似乎是为什么事情悬而未决的问题。 我通过实现一个pipe理信号的shell脚本解决了这个问题,并将它们转换为适当的omplayer命令(sorting – Jan所build议的蹩脚版本)。
解决scheme:问题是在player.send_signal()
。 信号沿着命令链处理不当,导致父应用程序挂起。 解决scheme是实现包装器的命令,不处理好信号。
另外:使用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=%sj=%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)
您正在启动执行shell命令的新进程,但不要等到完成。 你似乎依靠一个事实,即命令行过程本身是单一的,但是你的前端并不关心它,而且可以轻易地启动另一个命令行。
另一个问题可能是,您不要调用player.communicate()
,如果stdout
或stderr
被某些输出填充,您的过程可能会被阻止。
您正在尝试创建用于控制玩家的UI。 为此,将您的解决方案切实分解为前端和后端。 后端将作为播放器控制器,并提供类似的方法
要集成前端和后端,可以使用多个选项,其中之一是zerorpc
,如下所示: https : zerorpc
好处是,你可以很容易地创建其他前端(如命令行一,甚至远程)。
还有一个难题: proc.terminate()
和send_signal
。
下面的代码分叉了一个“玩家”(在这种情况下,只是一个有sleep
的shell),然后打印它的进程信息。 它等待片刻, terminate
玩家,然后验证过程不再,它已经不复存在了。
感谢@Jan Vlcinsky将proc.communicate()
添加到代码中。
(我正在运行Linux Mint LMDE,另一个Debian版本。)
# pylint: disable=E1101 import subprocess, time def show_procs(pid): print 'Process Details:' subprocess.call( 'ps -fl {}'.format(pid), shell=True, ) cmd = '/bin/sleep 123' player = subprocess.Popen(cmd, shell=True) print '* player started, PID',player.pid show_procs(player.pid) time.sleep(3) print '\n*killing player' player.terminate() player.communicate() show_procs(player.pid)
* player started, PID 20393 Process Details: FS 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: FS UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD