Python 使用os.kill()后,如何确定子进程何时终止?

Python 使用os.kill()后,如何确定子进程何时终止?,python,django,subprocess,Python,Django,Subprocess,我有一个Python程序(确切地说,是Django应用程序),它使用启动子流程。由于我的应用程序的架构限制,我无法使用来终止子流程,也无法检查流程何时终止。这是因为我无法在变量中保存对已启动子流程的引用 相反,当子流程启动时,我必须将流程idpid写入文件pidfile。当我想停止子流程时,我打开这个pidfile并使用来停止它 我的问题是:我如何知道子流程何时真正终止?使用signal.SIGTERM调用os.kill()后,需要大约1-2分钟才能最终终止。首先,我认为这对这个任务来说是正确的

我有一个Python程序(确切地说,是Django应用程序),它使用启动子流程。由于我的应用程序的架构限制,我无法使用来终止子流程,也无法检查流程何时终止。这是因为我无法在变量中保存对已启动子流程的引用

相反,当子流程启动时,我必须将流程id
pid
写入文件
pidfile
。当我想停止子流程时,我打开这个
pidfile
并使用来停止它

我的问题是:我如何知道子流程何时真正终止?使用
signal.SIGTERM
调用
os.kill()
后,需要大约1-2分钟才能最终终止。首先,我认为这对这个任务来说是正确的,但是当我在
os.kill()
之后调用它时,它给了我
OSError:[Errno 10]没有子进程

顺便说一下,我使用两个表单从HTML模板启动和停止子流程,程序逻辑在Django视图中。当我的应用程序处于调试模式时,异常会显示在我的浏览器中。我在视图中调用的子流程(
python manage.py crawlwebpages
)本身调用另一个子流程,即Scrapy crawler的实例,这一点可能也很重要。我将这个Scrapy实例的
pid
写入
pid文件
,这就是我想要终止的

以下是相关代码:

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
我能做什么?多谢各位

编辑:

根据给出的,我可以通过将函数
\u stop\u 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

我的解决方案是设置一个控制子流程的中间流程

因此,您的web请求(由于并行化,这些请求似乎都发生在不同的进程中)告诉控制进程启动给定的程序并观看它;一旦需要,他们就会询问情况

在最简单的情况下,这个进程将是一个打开UNIX域套接字(TCP/IP套接字也可以)并侦听它的进程。“web进程”连接到它,发送启动请求并获取唯一ID。之后,它可以使用此ID对新进程进行进一步查询


或者,它自己给出ID(或者如果只有一个进程,则根本不使用ID),因此不必保留一些变量ID。

检查进程是否仍在运行的常用方法是使用信号“0”终止它()。它对正在运行的作业不做任何操作,如果该进程不存在,则会引发
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
[jajcus@lolek~]$sleep 1000&
[1] 2405
[jajcus@lolek~]$python
Python 2.7.3(默认值,2012年5月11日,11:57:22)
[GCC 4.6.3 20120315(发布)]关于linux2
有关详细信息,请键入“帮助”、“版权”、“信用证”或“许可证”。
>>>导入操作系统
>>>os.kill(2405,0)
>>>os.kill(2405,15)
>>>os.kill(2405,0)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
OSError:[Errno 3]没有这样的过程

但是只要可能,调用方就应该保持被调用进程的父进程,并使用
wait()
函数族来处理其终止。这就是Popen对象所做的。

您以前试过吗?也许这个异常意味着给定的子流程不再存在…@glglgl:是的,我试过了。发生此异常时,根据活动监视器(我使用Mac OSX作为我的操作系统),相应的进程仍处于活动状态。1-2分钟后,它最终终止。@glglgl您不能使用waitpid,因为您的进程必须是该进程的父进程。如果他被迫使用pidfiles,他可能会从Popen以外的另一个进程调用waitpid。@JonasWielicki对,这是我写评论后想到的。因此,中间进程的概念是这样的。@JonasWielicki:正如您在我添加的代码中所看到的,我从同一个视图调用了
os.waitpid()
,因此它应该是同一个进程,而不是另一个进程。还是我误解了你?啊,现在我想我明白了。每次我提交两份表格中的一份时,这会发生在两个不同的过程中。这就是为什么
os.waitpid()
会给出如上所示的错误,对吗?根据我上面的代码,您将如何添加此控制过程?说到子流程和并行化,我还是个初学者。谢谢大家!@彼得斯塔尔加了一个食谱,邻居!(顺便说一句)丹科,纳什巴!:)我会试试这个。但我认为这对我的申请来说是一种过度的杀伤力。也许我会尝试一种完全不同的方法。太棒了,杰克,非常感谢你!:)你的想法解决了我的问题。你可以在上面看到我修改过的代码,现在可以使用了。唯一的缺陷是,我在控制台中仍然没有得到
OSError:[Errno 3]这样的进程,但这是一个小问题。@PeterStahl不会在另一个
中包装
os.kill(process\u id,signal.SIGTERM)
。。。除了
修复最后一个问题之外?哎呀,那是我的错。最后一个错误的来源完全不同,与您的代码无关。我很抱歉。再次感谢!:)