Linux 进程状态、孤儿进程和僵尸进程

Linux 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

进程执行时的间断性,决定了进程可能具有多种状态。

运行中的进程可能具有三种基本状态:

1 就绪状态(ready):进程已经获取了除CPU意外的所需资源,等待分配CPU资源;只要分配了CPU进程就可以执行。就绪进程可以按多个优先级来划分队列。

例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。

2 运行状态(Running)

进程占用CPU资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。

3 阻塞状态(Blocked)

由于进程等待某种条件(如I/O操作或进程同步)或者某事件发生,在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行

(tips:要理解网络IO的几种状态要先理解进程状态。)

    总之一句话 :运行占用CPU,就绪只差CPU,阻塞在等一件事。

进程还有  就绪挂起(ready suspended),阻塞挂起(blocked suspended),

     就绪/挂起:把就绪态的进程从内存中换出到虚拟内存中。进程在VM(虚拟内存)中,只要调入主存(物理内存)中就能运行。

     阻塞/挂起:把阻塞态的进程从内存中换出到虚拟内存中。当内存中所有进程都阻塞的时候,CPU就把其中一个阻塞的进程调到VM中。这个就是阻塞挂起。

五个问题帮助理解:

1 阻塞挂起的状态和阻塞状态 最大的不同点是什么?

        阻塞状态的进程在主内存中,阻塞挂起状态的进程在虚拟内存中。

2 阻塞挂起和就绪挂起 最大的不同点是什么?

     阻塞挂起 等待的事件发生之后便进入就绪挂起。最大的不同是等待的事件有没有发生。

3 什么情况下会使进程 由就绪转到就绪/挂起状态?

      多个就绪进程优先级比较低,有高优先级的阻塞(操作系统OS 认为很快就会就绪)进程和低优先级就绪进程时,系统会选择挂起低优先级就绪进程。

4 什么情况下会使进程 从阻塞/挂起变成阻塞状态

      当一个进程释放足够的内存时,系统会把一个高优先级的阻塞/挂起进程 调入内存。

5 什么情况下会使进程 从运行状态变成就绪挂起状态?

          对抢占式系统,当有高优先级阻塞挂起进程因事件出现而进入就绪挂起时,系统可能会把运行进程(优先级比较低)转到就绪挂起状态

         通常当分配给一个运行进程的时间片用完时,它将转换到就绪态。但是,如果由于位于阻塞/挂起队列中具有较高优先级的进程变得不再被阻塞,操作系统抢占这个进程,也可以直接把这个运行进程转换到就绪/挂起队列中,并释放一些内存空间。

   

尝试着画了一张图 来帮助自己记忆。

image.png

在Linux中man ps 可以看到的以下进程的状态的说明:

S 进程状态 Process Status .包括以下几种状态: 

D = uninterruptible sleep 不可中断 

R = running or runnable 运行或者就绪的

S = sleeping 睡眠 

T = traced or stopped 追踪或者停止 

Z = zombie 僵尸 进程结束,没有被其父进程回收。

X = dead 一般看不到

<    high-priority (高优先级)

N    low-priority (低优先级)

L   页面是否已锁定到内存中

s    代表这个进程是一个会话的领导进程

l    多线程(using CLONE_THREAD, like NPTL pthreads do)

+   代表前台进程组

进程组:进程组表示一组相互关联的进程,比如每个子进程都是父进程所在组的成员

会话:而会话是指共享同一个控制终端的一个或者多个进程组

比如,我们通过 SSH 登录服务器,就会打开一个控制终端(TTY),这个控制终端就对应一个会话。而我们在终端中运行的命令以及它们的子进程,就构成了一个个的进程组,其中,在后台运行的命令,构成后台进程组;在前台运行的命令,构成前台进程组。

二  orphan process  孤儿进程

     在类UNIX(unix-like)操作系统中,子进程是通过父进程创建的,子进程再创建新的进程。

子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。

当一个父进程由于正常完成工作而退出或由于其他情况被终止,它的一个或多个子进程却还在运行,那么那些子进程将成为孤儿进程。

为避免孤儿进程退出时无法释放所占用的资源而僵死,进程号为1的进程将会接受这些孤儿进程,这一过程也被称为“收养”(英语:re-parenting)

因为有1号大哥进程在“善后”,孤儿进程并不会有什么危害。

三  zombie process 僵尸进程

正常情况下,当一个进程创建了子进程后,它应该通过系统调用 wait() 或者 waitpid() 等待子进程结束,回收子进程的资源;而子进程在结束时,会向它的父进程发送 SIGCHLD 信号,所以,父进程还可以注册 SIGCHLD 信号的处理函数,异步回收资源。

僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程。释放子进程占用的资源,此时子进程就变成了僵尸进程。

任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理

僵尸进程的危害:

在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。

但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。

直到父进程通过wait / waitpid来取时才释放. 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。

僵尸进程的清理方案:

1 杀死僵尸进程的父进程

僵尸进程避免方案:

1 两次fork:而且使紧跟的子进程直接退出,是的孙子进程成为孤儿进程,从而init进程将负责清除这个孤儿进程。

2 主动忽略子进程的信号通知。通过 signal(SIGCHLD,SIG_IGN) 通知内核对子进程结束不关心,由内核回收。SIGCHLD 是子进程退出的时候像父进程发送的。SIG_IGN 表示信号处理方式为忽略。

这种方法常用于并发服务器性能提升。因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。

3 父进程调用wait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞。waitpid可以通过传递WNOHANG使父进程不阻塞立即返回。

4 如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。异步回收资源。

总结:孤儿进程 是指子进程还在运行,而父进程提前退出,导致子进程被1号进程善后.孤儿进程没什么危害。

  僵尸进程是指,父进程在子进程运行完毕后,没有回收其资源,导致僵尸进程一直存在,浪费进程号,浪费内存。

参考链接:https://www.cnblogs.com/wuchanming/p/4020463.html

                 https://unordered.org/timelines/59cd509019c01000

                http://jjplane.com/index.php/2017/10/13/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-%E9%9D%99%E6%AD%A2%E9%98%BB%E5%A1%9E%E6%B4%BB%E5%8A%A8%E9%98%BB%E5%A1%9E%E9%9D%99%E6%AD%A2%E5%B0%B1%E7%BB%AA%E6%B4%BB%E5%8A%A8%E5%B0%B1%E7%BB%AA/

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注