僵尸进程和孤儿进程(docker)

         当一个子进程终止后,它首先会变成一个“失效(defunct)”的进程,也称为“僵尸(zombie)”进程,等待父进程或系统收回(reap)。在Linux内核中维护了关于“僵尸”进程的一组信息(PID,终止状态,资源使用信息),从而允许父进程能够获取有关子进程的信息。如果不能正确回收“僵尸”进程,那么他们的进程描述符仍然保存在系统中,系统资源会缓慢泄露。

        大多数设计良好的多进程应用可以正确的收回僵尸子进程,比如NGINX master进程可以收回已终止的worker子进程。如果需要自己实现,则可利用如下方法:
       1. 利用操作系统的waitpid()函数等待子进程结束并请除它的僵死进程,
       2. 由于当子进程成为“defunct”进程时,父进程会收到一个SIGCHLD信号,所以我们可以在父进程中指定信号处理的函数来忽略SIGCHLD信号,或者自定义收回处理逻辑。

        如果父进程已经结束了,那些依然在运行中的子进程会成为“孤儿(orphaned)”进程。在Linux中Init进程(PID1)作为所有进程的父进程,会维护进程树的状态,一旦有某个子进程成为了“孤儿”进程后,init就会负责接管这个子进程。当一个子进程成为“僵尸”进程之后,如果其父进程已经结束,init会收割这些“僵尸”,释放PID资源。

        由于Docker容器的PID1进程是容器启动进程,处理那些“孤儿”进程和“僵尸”进程的过程需要我们思考。(1)对于docker容器来说,在容器内事先启动一个bash进程,然后使用docker exe方式在已经启动的容器内启动一个sleep 1000的进程,再在宿主机上杀死该容器的bash进程,查看宿主机上的进程列表会发现,bash进程已被杀死,而sleep进程虽然已经结束,但是没有被父进程回收,成为了“僵尸”状态。(2)而如果在docker容器内部,事先启动一个/bin/sh进程作为该容器的PID1进程,然后再docker exec 创建一个sleep 1000进程,在宿主机上杀死该容器的bash进程,查看宿主机上的进程列表会发现,bash进程和sleep进程都已被杀死和回收资源。

       产生上述两种情况的原因在于,sh/bash等应用可以自动清理僵尸进程。如果在容器中运行多个进程,PID1进程需要有能力接管“孤儿”进程并回收“僵尸”进程。我们可以:
      1. 利用自定义的init进程来进行进程管理
      2. bash/sh等缺省提供了进程管理能力,可以作为PID1进程来实现正确的进程回收。

你可能感兴趣的:(概念,学习,安全,经验分享,云计算)