【Linux】僵尸进程和孤儿进程

来源:互联网 发布:marginnote windows版 编辑:程序博客网 时间:2024/06/05 19:51

在我们进行关于僵尸进程和孤儿进程的分析前,先了解下进程都有哪些状态:

下面的状态在 fs/proc/array.c 文件里定义:

/** The task state array is a strange "bitmap" of* reasons to sleep. Thus "running" is zero, and* you can test for combinations of others with* simple bit tests.*/static const char * const task_state_array[] = {"R (running)", /* 0 */"S (sleeping)", /* 1 */"D (disk sleep)", /* 2 */"T (stopped)", /* 4 */"t (tracing stop)", /* 8 */"X (dead)", /* 16 */"Z (zombie)", /* 32 */};

R : running ,运行状态           并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。

S :sleeping ,睡眠状态           意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。需要强调的是此时进程并不是什么都不做,它执行了休眠代码,是可以被杀死的。

D :disk sleep ,,磁盘休眠状态        也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。与 S 相反,它是不可以被杀死的,中断它的方法只能是系统关机修复重启,或者“主动醒来”。

T :stopped ,终止状态        此时进程不做任何事。

t  :tracing stop ,跟踪终止状态         可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT  信号让进程继续运行。

例如,可以用下面的方法来停止或继续运行进程:
kill -SIGSTOP <pid>       进程状态从 R 到 T 
kill -SIGCONT <pid>      进程状态从 T  到 R
可以使用 gdb 终止进程来实现跟踪终止状态。

X :dead ,死亡状态             内核运行 kernel/exit.c 里的 do_exit() 函数返回的状态。这个状态只是一个返回状态,不会在任务列表里看到这个状态。

Z :Zombie ,僵死状态        当进程退出并且父进程(使用 wait() 系统调用)没有读取到子进程退出的返回代码时就会产生僵死进程。僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。

以上就是进程的状态分类,下面我们具体对僵尸进程和孤儿进程做以讨论。

僵尸进程一个子进程在其父进程没有调用 wait() 或 waitpid() 的情况下退出。这个子进程就是僵尸进程。如果其父进程还存在而一直不调用 wait ,则该僵尸进程将无法回收,等到其父进程退出后该进程将被 init 回收。

我们来创建一个维持30秒的僵死进程例子:








运行 ./myenv 时我们会发现先打印出来子进程的内容,即子进程先退出,30秒后父进程的内容才被打印,此时可以证明进程从子进程运行到父进程运行的中间时间段30秒,进程是处于僵死状态的,这样的子进程就是僵尸进程。

如果你不是很清楚父子进程分别为哪个的话,我们可以通过父子进程的 PID 验证一下这两者的关系。

同样地,我们在 env.c 中编写代码,然后调试、运行,观察分析结果。



其实只要不按下 Ctrl+C ,我们会发现程序一直在运行,不会停止,而且我们能够很清楚地看到只有一行的子进程 child ,它的 PID 是7855,PPID 即父进程是7854,我想这样表述你是不是就很清楚父子进程了呢,那么有人可能会疑惑5124是什么呢,很简单,对父进程 father 来说,它自己的 PID 是7854,它的父进程是 bash ,PPID 的值就是5124。

上面的图片是我用 ps -l 命令查看的优先级信息,我们会看到 bash 的 PID 就是5124,其余的暂时不做讨论。


孤儿进程一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为1)所收养,并由 init 进程对它们完成状态收集工作。

下面我们来创建一个孤儿进程的例子:

关于如何一步步创建的步骤,此处不再赘述,同上。





我们可以发现父进程先运行输出,休眠一秒后子进程运行,打印出来 pid 为 8533 ,ppid 为 8532 ,然后子进程休眠五秒,父进程运行结束后退出,此时父进程不会接收它,所以它会一直等待直到 init 进程收集它时才停止整个进程,上述的 init 即为 1 。可以证明无父进程接收的子进程就是孤儿进程。


这就是我对于僵尸进程和孤儿进程的理解,中间穿插了一些关于父子进程的知识,希望能够对你理解这两个进程有所帮助。





1 0