Python并发编程之多进程(孤儿,僵尸,守护进程)

七.孤儿进程

1.什么是孤儿进程

  • 当一个父进程创建了多个子进程, 子进程再创建子子进程等等
  • 父进程因正常运行完毕或其他情况被干掉的时候, 它的子进程就变成了孤儿进程
  • 为了避免孤儿进程完成任务后没有父亲通知操作系统回收资源
  • 于是 PID 为 "1"的顶级进程 systemd 就接手了这个孤儿进程
  • systemd 相当于一个孤儿院, 但凡是孤儿进程都会成为它的子进程

2.孤儿进程演示

  • 先在一个虚拟终端里开启一个 Bash 进程,把他当做父进程
  • 紧接着开启一个 “sleep 1000 &” 进程, 把它当做子进程
  • 然后在另一个虚拟终端查看这两个进程信息

孤儿进程bash和sleep演示

  • 再杀掉 sleep 的父进程 Bash 看看结果如何

杀掉父进程bash2

  • 图示

八.僵尸进程

1.什么是僵尸进程

  • 这是Linux出于好心的设计
  • 一个父进程开启了一堆子进程, 当子进程比父进程先运行完(死掉)
  • 操作系统会释放子进程占用的重型资源(内存空间, CPU资源, 打开的文件)
  • 但会保留子进程的关键信息(PID, 退出状态, 运行时间等)
  • 目的是为了让父进程能随时查看自己的子进程信息(不管该子进程有没有死掉)
  • 这种已经死掉的子进程都会进入僵尸状态, '‘僵尸进程’'是Linux系统的一种数据结构

ps : 任何正常结束的子进程都会进入到僵尸状态, 而被强制终止的进程的所有信息将会被清除

2.僵尸进程回收----概念

  • 操作系统保留子进程信息供父进程查看
  • 当父进程觉得不再需要查看的时候, 会向操作系统发送一个 wait / waitpid 系统调用
  • 于是操作系统再次清理僵尸进程的残余信息

3.僵尸进程回收----实际

  • 优秀的开源软件
这些软件在开启子进程时, 父进程内部会及时调用"wait" / "waitpid" 通知操作系统来回收僵尸进程
  • 水平良好的开发者
功底深厚,知道父进程要对子进程负责
会在父进程内部考虑到调用 "wait" / "waitpid" 通知操作系统回收僵尸进程
但是发起系统调用时间可能慢了一点
于是我们就可以使用 "ps aux | grep [z]+" 命令查看到僵尸进程
  • 水平非常低的开发者
技术半吊子,只知道开子进程,父进程也不结束,并在那一直开子进程,不知道什么是僵尸进程
系统调用 "wait" / "waitpid" 也没有听说过
于是计算机会堆积许多的僵尸进程,占用着大量的"pid",(每启动一个进程就会分配一个"pid号")
计算机进入一个奇怪的现象: 内存够用,硬盘充足,CPU空闲,但新的程序无法启动
这就是因为"PID"不够用了

4.如何清理僵尸进程

  • 针对良好的开发者
我们可以手动发信号给父进程: "# kill -CHLD [父进程的PID]"
通知父进程快点向操作系统发起系统调用 "wait" / "waitpid" 来清理变成僵尸的儿子们
  • 针对半吊子水平的开发者
这种情况子下,我们只能将父进程终结,因为你发给它的信号不会得到回应
父进程被杀死,"僵尸进程"将会变成"僵尸孤儿进程"
但凡是"孤儿进程"都会被Linux系统中"PID""1"的顶级进程"systemd"回收
"systemd"会发起系统调用 "wait" / "waitpid" 来通知操作系统清理僵尸进程
# Centos7 的顶级进程为 systemd
# Centos6 的顶级进程为 init

5.使用Process类制造僵尸进程

原本 multiprocessing 模块在你发起系统调用 start() 开启子进程的时候会自动检测当前状态下是否存在僵尸进程, 并将其回收, join() 调用也是一样, 我们可以查看这两个调用的源码进行查看 :

image-20210120172357714

image-20210120172459572

  • 我们可以让父进程创建子进程后暂停在原地什么事情都不做, 于是 multiprocessing 模块的底层机制都没有运行, 也就没法清除运行完毕并变成僵尸态的子进程, 下面再 Linux 上进行演示 :
# coding:utf-8
from multiprocessing import Process
import os,time

def task():
    print("子进程:%s"%os.getpid())
    time.sleep(4)  # 子进程 4 秒后结束变成僵尸进程

if __name__ == "__main__":
    for i in range(400):
        print("父进程:%s"%os.getpid())
        p = Process(target=task)
        p.start()
    time.sleep(100000)  # 让父进程停在原地什么也不做

使用 top 命令查看系统状态信息, 可以发现已经出现了 400 个僵尸进程

image-20210120183029242

我们可以通过 kill 刚运行的 py 文件将这些僵尸进程变成孤儿进程, 从而被 systemd 接管, systemd 再发起系统调用将其清除

九.守护进程

1.什么是守护进程

由主进程创建, 并会随着主进程的结束而结束

2.守护进程的生命周期

  • 进程之间是相互独立的, 守护进程会在主进程代码执行结束后就终止

  • 守护进程内无法再次开启子进程, 否则会抛出异常 : AssertionError: daemonic processes are not allowed to have children

from multiprocessing import Process
import os,time

class MyProcess(Process):
    def __init__(self,n):
        super().__init__()
        self.n = n

    def run(self) -> None:
        print(f'子进程:{os.getpid()}开始')
        time.sleep(2)
        print(f"子进程:{os.getpid()}结束")

if __name__ == '__main__':
    p = MyProcess(2)
    p.daemon = True  # 需要在 strat() 之前设置
    p.start()
    print(f"主进程:{os.getpid()}结束")  
    # 在当前主进程的代码已经运行完毕, 守护进程就会终止, 甚至守护进程还没来的急启动
'''输出
主进程:16924结束
'''

我们使用 sleep 让主进程简单延时一下好让子进程启动起来

from multiprocessing import Process
import os,time

class MyProcess(Process):
    def __init__(self,n):
        super().__init__()
        self.n = n

    def run(self) -> None:
        print(f'子进程:{os.getpid()}开始')
        time.sleep(2)
        print(f"子进程:{os.getpid()}结束")

if __name__ == '__main__':
    p = MyProcess(2)
    p.daemon = True
    p.start()
    time.sleep(1)  # 延时一秒, 足够操作系统将子进程开起来
    print(f"主进程:{os.getpid()}结束")

'''输出
子进程:8620开始
主进程:10480结束
'''

再次强调, 守护进程是在主进程的代码执行完毕终止

from multiprocessing import Process
import os,time

def Foo():
    print(f"Foo:{os.getpid()}-->111")
    time.sleep(1)
    print(f"Foo--->222")

def Bar():
    print(f"Bar:{os.getpid()}-->333")
    time.sleep(2)
    print(f"Bar--->444")

if __name__ == '__main__':
    p1 = Process(target=Foo)
    p2 = Process(target=Bar)

    p1.daemon = True  # 将 p1 设置守护进程
    p1.start()
    p2.start()
    print("------>end")
# 当运行到这一行的时候主进程代码已经运行完了, 那么守护进程也已经终止了, 与主进程在等着 p2 运行无关, 这时操作系统还没来的急启动 p1 这个子进程

'''输出
------>end
Bar:18124-->333
Bar--->444
'''

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给你骨质唱疏松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值