Mit*_*ren 18 python apache cgi fork
我正在尝试从CGI脚本启动后台进程.基本上,当提交表单时,CGI脚本将向用户指示正在处理他或她的请求,而后台脚本执行实际处理(因为处理往往需要很长时间.)我面临的问题是Apache在子脚本终止之前不会将父CGI脚本的输出发送到浏览器.
一位同事告诉我,我想做的事情是不可能的,因为没有办法阻止Apache等待CGI脚本的整个进程树死掉.但是,我也看到网上有很多引用"双叉"技巧,这个技巧应该可以完成.这个技巧在Stack Overflow的回答中简洁地描述,但我在其他地方看到了类似的代码.
这是我编写的用于测试Python中的双叉技巧的简短脚本:
import os
import sys
if os.fork():
print 'Content-type: text/html\n\n Done'
sys.exit(0)
if os.fork():
os.setsid()
sys.exit(0)
# Second child
os.chdir("/")
sys.stdout.close()
sys.stderr.close()
sys.stdin.close()
f = open('/tmp/lol.txt', 'w')
while 1:
f.write('test\n')
Run Code Online (Sandbox Code Playgroud)
如果我从shell运行它,它完全符合我的期望:原始脚本和第一个后代死亡,第二个后代继续运行,直到它被手动杀死.但是如果我通过CGI访问它,那么在我杀死第二个后代之后页面将不会加载,或者由于CGI超时而Apache杀死它.我也尝试替换第二sys.exit(0)带os._exit(0),但没有任何区别.
我究竟做错了什么?
Nas*_*nov 12
这种双重分叉方法是某种黑客行为,对我来说这表明不应该这样做:).无论如何,对于CGI.根据一般原则,如果事情太难实现,你可能会以错误的方式接近它.
幸运的是,你提供了你需要的背景信息 - 一个CGI调用来启动一些独立发生的处理并返回给调用者.确定 - 有unix命令就是这样 - 调度命令在特定时间(at)或CPU空闲时运行(batch).所以这样做:
import os
os.system("batch <<< '/home/some_user/do_the_due.py'")
# or if you don't want to wait for system idle,
# os.system("at now <<< '/home/some_user/do_the_due.py'")
print 'Content-type: text/html\n'
print 'Done!'
Run Code Online (Sandbox Code Playgroud)
你有它.请记住,如果stdout/stderr有一些输出,那么它将被邮寄给用户(这对于调试很有用,但是脚本可能应该保持安静).
PS.我只记得Windows也有版本at,所以通过对调用的微小修改,你也可以在windows下的apache下工作(相对于在Windows上不起作用的fork技巧).
PPS.确保不会/etc/at.deny从调度批处理作业中排除运行CGI的进程
我认为有两个问题:setsid在错误的地方并在其中一个瞬态子进行缓冲IO操作:
if os.fork():
print "success"
sys.exit(0)
if os.fork():
os.setsid()
sys.exit()
Run Code Online (Sandbox Code Playgroud)
你有原始的过程(祖父母,打印"成功"),中间父母和孙子("lol.txt").
该os.setsid()呼叫在中间母公司执行的孙子已经催生了之后.在创建孙子后,中间父母不能影响孙子的会话.试试这个:
print "success"
sys.stdout.flush()
if os.fork():
sys.exit(0)
os.setsid()
if os.fork():
sys.exit(0)
Run Code Online (Sandbox Code Playgroud)
这会在产生孙子之前创建一个新会话.然后中间父母去世,离开会话而没有进程组领导,确保打开终端的任何调用都将失败,确保终端输入或输出上没有任何阻塞,或者向孩子发送意外信号.
请注意,我也搬到success了祖父母; 有没有保证这孩子打完电话后第一次运行fork(2),并在运行的孩子会被催生的风险,并有可能尝试写输出到标准输出或标准错误,中间的父母可以有机会写之前success给远程客户端.
在这种情况下,流很快就会关闭,但是,在多个进程之间混合标准IO流必然会带来困难:如果可以的话,将它们保存在一个进程中.
编辑我发现了一个我无法解释的奇怪行为:
#!/usr/bin/python
import os
import sys
import time
print "Content-type: text/plain\r\n\r\npid: " + str(os.getpid()) + "\nppid: " + str(os.getppid())
sys.stdout.flush()
if os.fork():
print "\nfirst fork pid: " + str(os.getpid()) + "\nppid: " + str(os.getppid())
sys.exit(0)
os.setsid()
print "\nafter setsid pid: " + str(os.getpid()) + "\nppid: " + str(os.getppid())
sys.stdout.flush()
if os.fork():
print "\nsecond fork pid: " + str(os.getpid()) + "\nppid: " + str(os.getppid())
sys.exit(0)
#os.sleep(1) # comment me out, uncomment me, notice following line appear and dissapear
print "\nafter second fork pid: " + str(os.getpid()) + "\nppid: " + str(os.getppid())
Run Code Online (Sandbox Code Playgroud)
最后一行after second fork pid仅在os.sleep(1)呼叫被注释掉时出现.当呼叫保持不变时,最后一行永远不会出现在浏览器中.(但是否则所有内容都会打印到浏览器中.)
| 归档时间: |
|
| 查看次数: |
11185 次 |
| 最近记录: |