我有一个Python程序(确切地说,是一个Django应用程序),它使用subprocess.Popen
启动一个子进程。由于我的应用程序的架构限制,我无法使用Popen.terminate()
来终止子进程,而Popen.poll()
来检查进程何时终止。这是因为我无法在变量中保存对已启动子进程的引用。
相反,我必须在子进程启动时将进程标识pid
写入文件pidfile
。当我想停止子流程时,我打开这个pidfile
并使用os.kill(pid, signal.SIGTERM)
来停止它。
我的问题是:如何确定子进程何时真正终止?使用signal.SIGTERM
后,调用os.kill()
后最终需要大约1-2分钟才能终止。首先,我认为os.waitpid()
对于此任务是正确的,但是当我在os.kill()
之后调用它时,它会给我OSError: [Errno 10] No child processes
。
顺便说一下,我正在使用两个表单从HTML模板启动和停止子进程,程序逻辑在Django视图中。当我的应用程序处于调试模式时,异常会显示在我的浏览器中。知道我在视图中调用的子进程(python manage.py crawlwebpages
)本身调用另一个子进程,即Scrapy搜寻器的实例,这可能也很重要。我将此Scrapy实例的pid
写入pidfile
,这就是我要终止的内容。
以下是相关代码:
def process_main_page_forms(request):
if request.method == 'POST':
if request.POST['form-type'] == u'webpage-crawler-form':
template_context = _crawl_webpage(request)
elif request.POST['form-type'] == u'stop-crawler-form':
template_context = _stop_crawler(request)
else:
template_context = {
'webpage_crawler_form': WebPageCrawlerForm(),
'stop_crawler_form': StopCrawlerForm()}
return render(request, 'main.html', template_context)
def _crawl_webpage(request):
webpage_crawler_form = WebPageCrawlerForm(request.POST)
if webpage_crawler_form.is_valid():
url_to_crawl = webpage_crawler_form.cleaned_data['url_to_crawl']
maximum_pages_to_crawl = webpage_crawler_form.cleaned_data['maximum_pages_to_crawl']
program = 'python manage.py crawlwebpages' + ' -n ' + str(maximum_pages_to_crawl) + ' ' + url_to_crawl
p = subprocess.Popen(program.split())
template_context = {
'webpage_crawler_form': webpage_crawler_form,
'stop_crawler_form': StopCrawlerForm()}
return template_context
def _stop_crawler(request):
stop_crawler_form = StopCrawlerForm(request.POST)
if stop_crawler_form.is_valid():
with open('scrapy_crawler_process.pid', 'rb') as pidfile:
process_id = int(pidfile.read().strip())
print 'PROCESS ID:', process_id
os.kill(process_id, signal.SIGTERM)
os.waitpid(process_id, os.WNOHANG) # This gives me the OSError
print 'Crawler process terminated!'
template_context = {
'webpage_crawler_form': WebPageCrawlerForm(),
'stop_crawler_form': stop_crawler_form}
return template_context
我该怎么办?非常感谢你!
修改
根据the great answer提供的Jacek Konieczny,我可以通过将函数_stop_crawler(request)
中的代码更改为以下内容来解决我的问题:
def _stop_crawler(request):
stop_crawler_form = StopCrawlerForm(request.POST)
if stop_crawler_form.is_valid():
with open('scrapy_crawler_process.pid', 'rb') as pidfile:
process_id = int(pidfile.read().strip())
# These are the essential lines
os.kill(process_id, signal.SIGTERM)
while True:
try:
time.sleep(10)
os.kill(process_id, 0)
except OSError:
break
print 'Crawler process terminated!'
template_context = {
'webpage_crawler_form': WebPageCrawlerForm(),
'stop_crawler_form': stop_crawler_form}
return template_context
答案 0 :(得分:8)
检查进程是否仍在运行的常用方法是使用信号'0'来kill()它。它对正在运行的作业没有任何作用,如果该进程不存在,则会向OSError
引发errno=ESRCH
异常。
[jajcus@lolek ~]$ sleep 1000 &
[1] 2405
[jajcus@lolek ~]$ python
Python 2.7.3 (default, May 11 2012, 11:57:22)
[GCC 4.6.3 20120315 (release)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.kill(2405, 0)
>>> os.kill(2405, 15)
>>> os.kill(2405, 0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 3] No such process
但是只要有可能,调用者应该保留被调用进程的父级,并使用wait()
函数系列来处理它的终止。这就是Popen
对象的作用。
答案 1 :(得分:2)
我的解决方案是设置一个控制子处理的中间过程。
所以你的网络请求(由于并行化,所有这些似乎都发生在不同的进程中?)告诉控制进程启动一个给定的程序并观察它;他们会尽快询问状态如何。
在最简单的情况下,这个过程将是一个打开UNIX域套接字的过程(TCP / IP套接字也会这样做)并监听它。 “Web进程”连接到它,发送启动请求并获取唯一ID。之后,它可以使用此ID进一步查询新流程。
或者,它自己提供ID(或者它根本不使用ID,如果只有一个进程),所以不必保留一些变量ID。