26.并发编制【三】三种进程
【一】僵尸进程(有害)
1)概念
- 指完成了自己的任务,但父进程没有正确的释放它所占用的系统资源,导致它仍然存在与进程列表中,但已经停止运行
- 其会占据一定的系统内存,并在一定程度上影响系统的性能
2)解决办法(UNⅨ系统)
-
因此,UNⅨ提供了一种机制可以保证父进程可以在任意时刻获取子进程结束时的状态信息
-
- 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。
-
-
- 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)
-
-
- 直到父进程通过wait / waitpid来取时才释放.
-
-
- 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的
- 如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
-
-
任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。
-
- 这是每个子进程在结束时都要经过的阶段。
- 如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。
- 如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。
- 如果父进程在子进程结束之前退出,则子进程将由init接管。
- init将会以父进程的身份对僵尸状态的子进程进行处理。
3)示例
from multiprocessing import Process
import time
def run():
print(f'the run is beginning:>>>>')
time.sleep(1)
print(f'the run is ending:>>>>')
if __name__ == '__main__':
p = Process(target=run)
p.start()
print(f'这是主程序的执行方法:>>>>')
# 这是主程序的执行方法:>>>>
# the run is beginning:>>>>
# the run is ending:>>>>
4)危害场景
- 例如有个进程,它定期的产生一个子进程,这个子进程需要做的事情很少,做完它该做的事情之后就退出了,因此这个子进程的生命周期很短。但是,父进程只管生成新的子进程,至于子进程 退出之后的事情,则一概不闻不问。这样,系统运行上一段时间之后,系统中就会存在很多的僵死进程,倘若用ps命令查看的话,就会看到很多状态为Z的进程。
- 严格地来说,僵死进程并不是问题的根源,罪魁祸首是产生出大量僵死进程的那个父进程。
- 因此,当我们寻求如何消灭系统中大量的僵死进程时,答案就是把产生大 量僵死进程的那个元凶枪毙掉(也就是通过kill发送SIGTERM或者SIGKILL信号啦)。枪毙了元凶进程之后,它产生的僵死进程就变成了孤儿进 程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源。
- 这样,这些已经僵死的孤儿进程就能瞑目而去了
5)解决方法
- 杀死父进程
- 对开启的子进程应该记得使用join,join会回收僵尸进程
【二】孤儿进程(无害)
1)概念
- 指父进程在子进程终止之前就已经退出,导致子进程失去了父进程通信的能力
- 这些孤儿进程将被init进程接管,init进程会等待他的状态信息并释放其系统资源
2)示例
-
子进程存活,父进程意外死亡
-
- 没有父进程来帮助回收 PID 号
-
解决办法
-
- 操作系统会开设一个儿童福利院(init 进程)专门管理孤儿进程回收相关资源
import os
import sys
import time
if __name__ == '__main__':
pid = os.getpid()
ppid = os.getppid()
print('我是父进程 :>>> ', 'pid', pid, 'ppid', ppid)
pid = os.fork()
# 执行pid=os.fork()则会生成一个子进程
# 返回值pid有两种值:
# 如果返回的pid值为0,表示在子进程当中
# 如果返回的pid值>0,表示在父进程当中
if pid > 0:
print('父进程终止')
sys.exit(0)
# 保证主线程退出完毕
time.sleep(1)
print('我是子进程 :>>>> ', os.getpid(), os.getppid())
# 子进程已经被pid为1的init进程接收了
# 所以僵尸进程在这种情况下是不存在的,存在只有孤儿进程而已
# 孤儿进程声明周期结束自然会被init来销毁。
# 我是父进程 :>>> pid 86270 ppid 61564
# 父进程终止
【三】守护进程
1)概念
-
是在计算机系统启动时就已经运行,并且一直在后台运行的一类特殊进程
-
其通常不与用户之间交互,也不接受标准输入和输出,而是在后台执行某种任务或提供某种服务
-
只要系统不关机,其就会一直在后台运行,知道系统关闭或被停止
-
常见的守护进程包括网络服务 (如 web 服务器、邮件服务器、 ftp 服务器等)、日志记录系统 (如系统日志服务、应用程序日志服务等) 等。
2)示例
主进程死亡,子进程必死
from multiprocessing import Process
import time
def task(name):
print(f'皇太妃:>>{name}>>正常存活')
time.sleep(2)
print(f'皇太妃:>>{name}>>正常死亡')
if __name__ == '__main__':
print(f'皇帝 :>>> ChiMeng >>> 执掌江山')
p = Process(target=task, args=('dream',))
# 加一个守护进程 :子进程随着主进程结束而结束
# 守护进程加的位置一定要在 start 之前
# 在启动之前为当前子线程添加额外的参数和限制
# 将进程 p 设置成守护进程
p.daemon = True
p.start()
print(f'皇帝 :>>> ChiMeng >>> 寿终正寝')
# 皇帝 :>>> ChiMeng >>> 执掌江山
# 皇帝 :>>> ChiMeng >>> 寿终正寝