当父进程在循环中工作时,如何避免僵尸完成子进程

发布于 2025-01-13 23:32:37 字数 5994 浏览 2 评论 0原文

我正在寻找一个好的解决方案,以避免当父进程仍在工作时出现 ZOMBIE 子进程。例如,父级正在循环中工作并正在检查某些条件。如果条件满足,父进程运行子进程。

现在我编写了简单的 python 脚本:

File main.py:

#!/usr/bin/env python3
# coding=utf-8

import os
import psutil
import subprocess
import time
import signal
import setproctitle


def main():

    python_venv = 'python3'
    print(f"{__file__}  - Run proc 1")
    a1 = [python_venv, 'proc1.py']
    string_do_wywolania_part2 = ['arg1'  , 'arg2']
    a2 = a1 + string_do_wywolania_part2
    p = subprocess.Popen(a2, preexec_fn=os.setpgrp)

    print(f"{__file__}  - Run proc 2")
    a1 = [python_venv, 'proc2.py']
    string_do_wywolania_part2 = ['arg1'  , 'arg2']
    a2 = a1 + string_do_wywolania_part2
    p = subprocess.Popen(a2, preexec_fn=os.setpgrp)

    pid_id = os.getpid()
    ppid_id = os.getppid()
    print(f"{__file__} - pid_id  : {pid_id} - ppid_id  : {ppid_id}")

    parent = psutil.Process(pid_id)
    print(f"{__file__} : parent  : {parent}")

    t = 6
    print(f"{__file__} - I am waiting {t} sec.")
    time.sleep(t)



if __name__ == "__main__":

    "call sub processes"
    setproctitle.setproctitle('python3 - main')
    main()

    while True:

        k = 1
        pid_id = os.getpid()
        parent = psutil.Process(pid_id)
        print()
        for child in parent.children(recursive=True):  # or parent.children() for recursive=False
            print(f'{__file__} - {child}  = {child.status()}')
            k +=1
        time.sleep(5)


    print(f'{__file__} : Finished main')

File proc1.py

#!/usr/bin/env python3
# coding=utf-8
import os
import sys
import time
import setproctitle


if __name__ == "__main__":
    setproctitle.setproctitle('python3 - proc1')
    pid = os.getpid()
    ppid = os.getppid()
    print(f"{__file__} - Process 1 is running")
    print(f"{__file__} - pid  : {pid} - ppid  : {ppid}")
    time.sleep(5)
    print(f"{__file__} : Finished process 1  : {pid}")
    sys.exit(0)

proc2.file:

#!/usr/bin/env python3
# coding=utf-8

import os
import sys
import time
import subprocess
import signal
import setproctitle


if __name__ == "__main__":
    setproctitle.setproctitle('python3 - proc2')
    print(f"{__file__} - Process 2 is running")
    try:
        pid = os.getpid()
        ppid = os.getppid()
        print(f"{__file__} - pid  : {pid} - ppid  : {ppid}")
        print(f"{__file__}  - Run proc 3")

        python_venv = 'python3'
        a1 = [python_venv, 'proc3.py']
        string_do_wywolania_part2 = ['arg1'  , 'arg2']
        a2 = a1 + string_do_wywolania_part2
        p = subprocess.Popen(a2, preexec_fn=os.setpgrp)
        time.sleep(15)
    except Exception as e:
        print(e)

    time.sleep(7)
    print(f"{__file__} : Finished process 2  : {pid}")
    sys.exit(0)

和最后一个 proc3.py

#!/usr/bin/env python3
# coding=utf-8

import os
import sys
import time
import setproctitle


if __name__ == "__main__":
    ""
    setproctitle.setproctitle('python3 - proc3')
    pid = os.getpid()
    ppid = os.getppid()
    print(f"{__file__} - Process 3 is running")
    print(f"{__file__} - pid  : {pid} -  ppid  : {ppid}")
    time.sleep(10)
    print(f"{__file__} : Finished process 3  : {pid}")

    sys.exit(0)

当我运行 python3 main.py 时,我得到如下显示:

main.py  - Run proc 1
main.py  - Run proc 2
main.py - pid_id  : 5696 - ppid_id  : 4763
main.py : parent  : psutil.Process(pid=5696, name='python3 - main', status='running', started='14:26:46')
main.py - I am waiting 6 sec.
proc1.py - Process 1 is running
proc1.py - pid  : 5697 - ppid  : 5696
proc2.py - Process 2 is running
proc2.py - pid  : 5698 - ppid  : 5696
proc2.py  - Run proc 3
proc3.py - Process 3 is running
proc3.py - pid  : 5699 -  ppid  : 5698
proc1.py : Finished process 1  : 5697

main.py - psutil.Process(pid=5697, name='python3 - proc1', status='zombie', started='14:26:46')  = zombie
main.py - psutil.Process(pid=5698, name='python3 - proc2', status='sleeping', started='14:26:46')  = sleeping
main.py - psutil.Process(pid=5699, name='python3 - proc3', status='sleeping', started='14:26:46')  = sleeping
proc3.py : Finished process 3  : 5699

main.py - psutil.Process(pid=5697, name='python3 - proc1', status='zombie', started='14:26:46')  = zombie
main.py - psutil.Process(pid=5698, name='python3 - proc2', status='sleeping', started='14:26:46')  = sleeping
main.py - psutil.Process(pid=5699, name='python3 - proc3', status='zombie', started='14:26:46')  = zombie

main.py - psutil.Process(pid=5697, name='python3 - proc1', status='zombie', started='14:26:46')  = zombie
main.py - psutil.Process(pid=5698, name='python3 - proc2', status='sleeping', started='14:26:46')  = sleeping
main.py - psutil.Process(pid=5699, name='python3 - proc3', status='zombie', started='14:26:46')  = zombie

main.py - psutil.Process(pid=5697, name='python3 - proc1', status='zombie', started='14:26:46')  = zombie
main.py - psutil.Process(pid=5698, name='python3 - proc2', status='sleeping', started='14:26:46')  = sleeping
main.py - psutil.Process(pid=5699, name='python3 - proc3', status='zombie', started='14:26:46')  = zombie
proc2.py : Finished process 2  : 5698

main.py - psutil.Process(pid=5697, name='python3 - proc1', status='zombie', started='14:26:46')  = zombie
main.py - psutil.Process(pid=5698, name='python3 - proc2', status='zombie', started='14:26:46')  = zombie

如您所见,proc3 首先是僵尸但是当他的父进程完成时,proc 3 消失。 但我仍然有 proc1 和 proc2 作为僵尸。

如何调用child的子进程并避免它们在完成后属于僵尸? 我读到了有关 call、popen 和 preexec_fn 的信息,并且我应该 del subprocess.popen 对象,但我不知道哪一个是正确的。

你能告诉我我该怎么办吗?

编辑:

while True:
     if some_condition:
        p = subprocess.Popen()

        # what to do to avoid a zombie childs

     for proc in processes_list:
        if proc.poll() == 0:
            print(f'--> I am terminate {proc} --> {proc.pid}')
            proc.terminate()
            processes_list.remove(proc)

I am searching a good solution for avoiding a ZOMBIE child processes when parent is still working. For example parent is working in loop and is checking some conditions. If conditions are acomplished, parent process run subprocess.

Now I wrote simple python scripts:

File main.py:

#!/usr/bin/env python3
# coding=utf-8

import os
import psutil
import subprocess
import time
import signal
import setproctitle


def main():

    python_venv = 'python3'
    print(f"{__file__}  - Run proc 1")
    a1 = [python_venv, 'proc1.py']
    string_do_wywolania_part2 = ['arg1'  , 'arg2']
    a2 = a1 + string_do_wywolania_part2
    p = subprocess.Popen(a2, preexec_fn=os.setpgrp)

    print(f"{__file__}  - Run proc 2")
    a1 = [python_venv, 'proc2.py']
    string_do_wywolania_part2 = ['arg1'  , 'arg2']
    a2 = a1 + string_do_wywolania_part2
    p = subprocess.Popen(a2, preexec_fn=os.setpgrp)

    pid_id = os.getpid()
    ppid_id = os.getppid()
    print(f"{__file__} - pid_id  : {pid_id} - ppid_id  : {ppid_id}")

    parent = psutil.Process(pid_id)
    print(f"{__file__} : parent  : {parent}")

    t = 6
    print(f"{__file__} - I am waiting {t} sec.")
    time.sleep(t)



if __name__ == "__main__":

    "call sub processes"
    setproctitle.setproctitle('python3 - main')
    main()

    while True:

        k = 1
        pid_id = os.getpid()
        parent = psutil.Process(pid_id)
        print()
        for child in parent.children(recursive=True):  # or parent.children() for recursive=False
            print(f'{__file__} - {child}  = {child.status()}')
            k +=1
        time.sleep(5)


    print(f'{__file__} : Finished main')

File proc1.py

#!/usr/bin/env python3
# coding=utf-8
import os
import sys
import time
import setproctitle


if __name__ == "__main__":
    setproctitle.setproctitle('python3 - proc1')
    pid = os.getpid()
    ppid = os.getppid()
    print(f"{__file__} - Process 1 is running")
    print(f"{__file__} - pid  : {pid} - ppid  : {ppid}")
    time.sleep(5)
    print(f"{__file__} : Finished process 1  : {pid}")
    sys.exit(0)

proc2.file:

#!/usr/bin/env python3
# coding=utf-8

import os
import sys
import time
import subprocess
import signal
import setproctitle


if __name__ == "__main__":
    setproctitle.setproctitle('python3 - proc2')
    print(f"{__file__} - Process 2 is running")
    try:
        pid = os.getpid()
        ppid = os.getppid()
        print(f"{__file__} - pid  : {pid} - ppid  : {ppid}")
        print(f"{__file__}  - Run proc 3")

        python_venv = 'python3'
        a1 = [python_venv, 'proc3.py']
        string_do_wywolania_part2 = ['arg1'  , 'arg2']
        a2 = a1 + string_do_wywolania_part2
        p = subprocess.Popen(a2, preexec_fn=os.setpgrp)
        time.sleep(15)
    except Exception as e:
        print(e)

    time.sleep(7)
    print(f"{__file__} : Finished process 2  : {pid}")
    sys.exit(0)

and the last one proc3.py

#!/usr/bin/env python3
# coding=utf-8

import os
import sys
import time
import setproctitle


if __name__ == "__main__":
    ""
    setproctitle.setproctitle('python3 - proc3')
    pid = os.getpid()
    ppid = os.getppid()
    print(f"{__file__} - Process 3 is running")
    print(f"{__file__} - pid  : {pid} -  ppid  : {ppid}")
    time.sleep(10)
    print(f"{__file__} : Finished process 3  : {pid}")

    sys.exit(0)

When I am running python3 main.py i am getting displays like bellow:

main.py  - Run proc 1
main.py  - Run proc 2
main.py - pid_id  : 5696 - ppid_id  : 4763
main.py : parent  : psutil.Process(pid=5696, name='python3 - main', status='running', started='14:26:46')
main.py - I am waiting 6 sec.
proc1.py - Process 1 is running
proc1.py - pid  : 5697 - ppid  : 5696
proc2.py - Process 2 is running
proc2.py - pid  : 5698 - ppid  : 5696
proc2.py  - Run proc 3
proc3.py - Process 3 is running
proc3.py - pid  : 5699 -  ppid  : 5698
proc1.py : Finished process 1  : 5697

main.py - psutil.Process(pid=5697, name='python3 - proc1', status='zombie', started='14:26:46')  = zombie
main.py - psutil.Process(pid=5698, name='python3 - proc2', status='sleeping', started='14:26:46')  = sleeping
main.py - psutil.Process(pid=5699, name='python3 - proc3', status='sleeping', started='14:26:46')  = sleeping
proc3.py : Finished process 3  : 5699

main.py - psutil.Process(pid=5697, name='python3 - proc1', status='zombie', started='14:26:46')  = zombie
main.py - psutil.Process(pid=5698, name='python3 - proc2', status='sleeping', started='14:26:46')  = sleeping
main.py - psutil.Process(pid=5699, name='python3 - proc3', status='zombie', started='14:26:46')  = zombie

main.py - psutil.Process(pid=5697, name='python3 - proc1', status='zombie', started='14:26:46')  = zombie
main.py - psutil.Process(pid=5698, name='python3 - proc2', status='sleeping', started='14:26:46')  = sleeping
main.py - psutil.Process(pid=5699, name='python3 - proc3', status='zombie', started='14:26:46')  = zombie

main.py - psutil.Process(pid=5697, name='python3 - proc1', status='zombie', started='14:26:46')  = zombie
main.py - psutil.Process(pid=5698, name='python3 - proc2', status='sleeping', started='14:26:46')  = sleeping
main.py - psutil.Process(pid=5699, name='python3 - proc3', status='zombie', started='14:26:46')  = zombie
proc2.py : Finished process 2  : 5698

main.py - psutil.Process(pid=5697, name='python3 - proc1', status='zombie', started='14:26:46')  = zombie
main.py - psutil.Process(pid=5698, name='python3 - proc2', status='zombie', started='14:26:46')  = zombie

As you see, proc3 first is zombie but when parent process for him was finished, proc 3 disappear.
But still I have proc1 and proc2 as zombies.

How to call child's sub processes and avoid them to belong to zombie after finish?
I read info about call, popen and preexec_fn and that I should del subprocess.popen object, but I do not know which is right one.

Can you tell me what should I do?

EDITED:

while True:
     if some_condition:
        p = subprocess.Popen()

        # what to do to avoid a zombie childs

     for proc in processes_list:
        if proc.poll() == 0:
            print(f'--> I am terminate {proc} --> {proc.pid}')
            proc.terminate()
            processes_list.remove(proc)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

叫思念不要吵 2025-01-20 23:32:37

总结一下:

父进程必须等待子进程的退出状态。如果父进程在等待其子进程之前退出,那么最终会出现僵尸进程。

您已经考虑过拥有一个名为 processes_list< 的 Popen 对象列表/code>

您可以将 Popen.wait 与超时一起使用,例如:

for proc in processes_list:
    try:
        # use Popen.wait or Popen.communicate if using pipes.
        proc.wait(timeout=1)
        # proc.returncode contains the exit code of the child
        # no need to track anymore finished
        processes_list.remove(proc)
    except subprocess.TimeoutExpired as to:
        pass

如果您在编辑的代码中使用 Popen.poll ,那么您应该检查 None code> 这意味着孩子是尚未完成,所以不要将其从列表中删除。您在检查 0 但如果返回代码是其他内容怎么办?

while True:

    if some_condition:
        p = subprocess.Popen()
        processes_list.append(p)


    for proc in processes_list:
        # you can save the return value, although proc.returncode should have it as well
        p = proc.poll()
        # None means child is not finished yet, will poll again next round
        if p is not None:
            # again proc.returncode also contains the return code
            print(f'--> I am terminate {proc} --> {proc.pid}')
            processes_list.remove(proc)

最后,在 proc2 文件上同样适用,您必须等待子进程完成,然后让父进程本身退出。这可以通过 Popen.wait 来实现:

#!/usr/bin/env python3
# coding=utf-8

import os
import sys
import time
import subprocess
import signal
import setproctitle


if __name__ == "__main__":
    setproctitle.setproctitle('python3 - proc2')
    print(f"{__file__} - Process 2 is running")
    try:
        pid = os.getpid()
        ppid = os.getppid()
        print(f"{__file__} - pid  : {pid} - ppid  : {ppid}")
        print(f"{__file__}  - Run proc 3")

        python_venv = 'python3'
        a1 = [python_venv, 'proc3.py']
        string_do_wywolania_part2 = ['arg1'  , 'arg2']
        a2 = a1 + string_do_wywolania_part2
        p = subprocess.Popen(a2, preexec_fn=os.setpgrp)
        # no need to sleep as this does not guarantee that the child is done
        #time.sleep(15)
        # this will block until the child is done.
        # if you use poll here it will most probably result in a zombie. 
        p.wait()
        

        
    except Exception as e:
        print(e)

    time.sleep(7)
    print(f"{__file__} : Finished process 2  : {pid}")
    sys.exit(0)

To sum up:

A parent process must wait on the exit status of the child. If the parent exits before waiting on it's children then you end up with zombie processes.

You have already considered having a list of Popen objects called processes_list

You can use Popen.wait with a timeout like:

for proc in processes_list:
    try:
        # use Popen.wait or Popen.communicate if using pipes.
        proc.wait(timeout=1)
        # proc.returncode contains the exit code of the child
        # no need to track anymore finished
        processes_list.remove(proc)
    except subprocess.TimeoutExpired as to:
        pass

If you use Popen.poll as in your edited code then you should check for None which means that the child is not finished yet, so don't remove it from the list. You where checking for 0 but what if the return code is something else?

while True:

    if some_condition:
        p = subprocess.Popen()
        processes_list.append(p)


    for proc in processes_list:
        # you can save the return value, although proc.returncode should have it as well
        p = proc.poll()
        # None means child is not finished yet, will poll again next round
        if p is not None:
            # again proc.returncode also contains the return code
            print(f'--> I am terminate {proc} --> {proc.pid}')
            processes_list.remove(proc)

Finally on the proc2 file the same applies you must wait for the child process to finish and then let the parent itself exit. This can be achived with Popen.wait:

#!/usr/bin/env python3
# coding=utf-8

import os
import sys
import time
import subprocess
import signal
import setproctitle


if __name__ == "__main__":
    setproctitle.setproctitle('python3 - proc2')
    print(f"{__file__} - Process 2 is running")
    try:
        pid = os.getpid()
        ppid = os.getppid()
        print(f"{__file__} - pid  : {pid} - ppid  : {ppid}")
        print(f"{__file__}  - Run proc 3")

        python_venv = 'python3'
        a1 = [python_venv, 'proc3.py']
        string_do_wywolania_part2 = ['arg1'  , 'arg2']
        a2 = a1 + string_do_wywolania_part2
        p = subprocess.Popen(a2, preexec_fn=os.setpgrp)
        # no need to sleep as this does not guarantee that the child is done
        #time.sleep(15)
        # this will block until the child is done.
        # if you use poll here it will most probably result in a zombie. 
        p.wait()
        

        
    except Exception as e:
        print(e)

    time.sleep(7)
    print(f"{__file__} : Finished process 2  : {pid}")
    sys.exit(0)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文