为什么subprocess.Popen阻塞?(Why is subprocess.Popen blocking?)

我有一个处理登录的python cgi脚本,这是因为我的网站是三个(学校)网站的组合,并且在我的网站可以使用之前需要从这些网站提取数据。 这个提取需要2分钟,所以我想制作一个花哨的(半假的)加载屏幕。

我的注册码以:

import subprocess token = "".join(random.choice( string.ascii_lowercase + string.digits + string.ascii_uppercase) for _ in range(5)) #generate 5 random characters #run initScript subprocess.Popen("python {}/python/initUser.py {} {}".format( os.getcwd(), uid,token), shell=True, stdin=None, stdout=None, stderr=None, close_fds=True) print "Content-type: text/html" print "Location: registerLoading.php?token={}".format(token) print sys.exit(0)

子进程线被盗: 运行进程,不要等待

但是子进程线仍然阻塞,我无法弄清楚原因。

我正在开发ubuntu 16.04,它将在raspbarry pi 3上运行(这解释了加载时间)

I have an python cgi script that handles login, this is because my website is three (school) websites combined and before my website can be used the data needs to be extracted from those websites. This extraction takes 2 minutes so I want to make a fancy (semi-fake) loading screen.

My register code ends with:

import subprocess token = "".join(random.choice( string.ascii_lowercase + string.digits + string.ascii_uppercase) for _ in range(5)) #generate 5 random characters #run initScript subprocess.Popen("python {}/python/initUser.py {} {}".format( os.getcwd(), uid,token), shell=True, stdin=None, stdout=None, stderr=None, close_fds=True) print "Content-type: text/html" print "Location: registerLoading.php?token={}".format(token) print sys.exit(0)

With the subprocess line stolen from: Run Process and Don't Wait

But the subprocess line is still blocking and I can't figure out why.

I'm developing on ubuntu 16.04, and it's going to run on an raspbarry pi 3 (that explains the loading time)

最满意答案

close_fds对标准输出没有影响。 你需要devnull文件句柄(Python 3.3+中的subprocess.DEVNULL),以便该脚本的stdout在exit调用时关闭:

subprocess.Popen( ["python", "python/initUser.py", uid, token], stdin=None, stdout=open(os.devnull, 'wb'), stderr=open(os.devnull, 'wb'))

请注意,我也用列表形式替换了shell命令。 这使代码对命令注入安全 - 以前,每个用户都可以在Web服务器上运行任意shell命令。

此外,您可能还想加强令牌的安全性。 5个字符可以被强制使用,但更重要的是, random.choice不是加密安全的。 使用random.SystemRandom().choice代替Python 3.6+中更现代的secrets.token_urlsafe 。

close_fds has no effect on stdout. You want devnull file handles (subprocess.DEVNULL in Python 3.3+), so that that the stdout of this script is closed with the call to exit:

subprocess.Popen( ["python", "python/initUser.py", uid, token], stdin=None, stdout=open(os.devnull, 'wb'), stderr=open(os.devnull, 'wb'))

Note that I also replaced the shell command with a list form. This makes the code safe against command injection - previously, every user could run arbitrary shell commands on your webserver.

In addition, you may also want to beef up the security of the token. 5 characters can be brute-forced, but much more importantly, random.choice is not cryptographically secure. Use random.SystemRandom().choice instead, or the much more modern secrets.token_urlsafe in Python 3.6+.

更多推荐