前言:linux 下进程的运作方式
通俗解释:
linux 下进程都有父子关系,儿子死了,父亲负责收尸。如果父亲不管,要通知福利院来收尸. 如果父亲连通知都不做,那它那个死掉的儿子就是僵尸。
如果你只管生,不管死,儿子死的尸横遍野,肯定是你管理不当了。
linux 用 fork 创建子进程,用wait或waitpid 收尸。用kill 杀死进程(僵尸是杀不掉的)。
下面看看学术性详细描述:
每个linux 进程在进程表里都有一个进入点(entry),核心进程执行该进程时使用到的一切信息都存储在进入点。
当用 ps 命令察看系统中的进程信息时,看到的就是进程表中的相关数据。
当以fork()系统调用建立一个新的进程后,核心进程就会在进程表中给这个新进程分配一个进入点,然后将相关信息存储在该进入点所对应的进程表内。
这些信息中有一项是其父进程的识别码。
子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。
那么会不会因为父进程太忙来不及 wait 子进程,或者说不知道子进程什么时候结束,而丢失子进程结束时的状态信息呢?
不会。因为linux 提供了一种机制可以保证,只要父进程想知道子进程结束时的状态信息,就可以得到。
这种机制就是:当子进程走完了自己的生命周期后,它会执行exit()系统调用,内核释放该进程所有的资源,
包括打开的文件,占用的内存等。但是仍然为其保留一定的信息,包括进程号(the process ID),退出码(exit code),退出状态(the terminationstatus of the process)
运行时间the amount of CPU time taken by the process等),这些数据会一直保留到系统将它传递给它的父进程为止,直到父进程通过wait / waitpid来取时才释放。
也就是说,当一个进程死亡时,它并不是完全的消失了。进程终止,它不再运行,但是还有一些残留的数据等待父进程收回。
当父进程 fork() 一个子进程后,它必须用 wait() (或者 waitpid())等待子进程退出。正是这个 wait() 动作来让子进程的残留数据消失。
但是如果父进程先于子进程结束了,则子进程会认init 进程为父进程。
一、什么是僵尸进程
僵尸进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。
当用ps命令观察进程的执行状态时,看到这些进程的状态栏为defunct。
二、僵尸进程的危害
调度程序无法选中Defunct 进程,所以不能用kill命令删除Defunct 进程
defunct进程占用系统的内存资源,影响系统的性能, 而且如果其数目太多,
将因为没有可用的进程号而导致系统不能产生新的进程。 导致系统瘫痪。
此时的错误为:
fork: retry: Resource temporarily unavailable
查看:
ps -ef <defunct> 太多
三、僵尸进程的产生
如果子进程死亡时父进程没有 wait(),通常用 ps 可以看到它被显示为“<defunct>”,这样就产生了僵尸进程。它将永远保持这样直到父进程 wait()。
由此可见,defunct进程的出现时间是在子进程终止后,但是父进程尚未读取这些数据之前。
四: 孤儿进程(orphan process)
但是如果该进程的父进程已经先结束了,那么这个进程就成为孤儿进程,那么该进程就不会变成僵尸进程。
因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看看有没有哪个进程是刚刚结束的这个进程的子进程,
如果是的话,就由Init进程来接管他,成为他的父进程,从而保证每个进程都会有一个父进程。而Init进程会自动wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程。
就是说,孤儿进程会由init 进程接管。
如果一个程序设计上有缺陷,就可能导致某个进程的父进程一直处于睡眠状态或是陷入死循环,父进程没有wait子进程,也没有终止以使Init接管,
该子进程执行结束后就变成了defunct进程,这个defunct 进程可能会一直留在系统中直到系统重新启动。
四、如何避免僵尸进程
a、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
b. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。
c. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,
那么子进程结束后,内核会回收,并不再给父进程发送信号
下面给出一个实例, 放开两处注释中的任一处,都不会产生僵尸进程。 用ps -ef |grep test 观察。
[hjj@hjj ~/test]$ cat test.cpp
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
// signal(SIGCLD, SIG_IGN);
int pid = fork ();
if (pid == 0)
{
printf("child exit now ...");
_exit (0);
}
else
{
/*
int i = 0;
int status = 0;
while (!waitpid(pid, &status, WNOHANG))
{
printf ("father waiting%d\n", ++i);
sleep (1);
}
*/
while (1)
{
printf ("father running ...\n");
sleep (1);
}
return 0;
}
}
通俗解释:
linux 下进程都有父子关系,儿子死了,父亲负责收尸。如果父亲不管,要通知福利院来收尸. 如果父亲连通知都不做,那它那个死掉的儿子就是僵尸。
如果你只管生,不管死,儿子死的尸横遍野,肯定是你管理不当了。
linux 用 fork 创建子进程,用wait或waitpid 收尸。用kill 杀死进程(僵尸是杀不掉的)。
下面看看学术性详细描述:
每个linux 进程在进程表里都有一个进入点(entry),核心进程执行该进程时使用到的一切信息都存储在进入点。
当用 ps 命令察看系统中的进程信息时,看到的就是进程表中的相关数据。
当以fork()系统调用建立一个新的进程后,核心进程就会在进程表中给这个新进程分配一个进入点,然后将相关信息存储在该进入点所对应的进程表内。
这些信息中有一项是其父进程的识别码。
子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。
那么会不会因为父进程太忙来不及 wait 子进程,或者说不知道子进程什么时候结束,而丢失子进程结束时的状态信息呢?
不会。因为linux 提供了一种机制可以保证,只要父进程想知道子进程结束时的状态信息,就可以得到。
这种机制就是:当子进程走完了自己的生命周期后,它会执行exit()系统调用,内核释放该进程所有的资源,
包括打开的文件,占用的内存等。但是仍然为其保留一定的信息,包括进程号(the process ID),退出码(exit code),退出状态(the terminationstatus of the process)
运行时间the amount of CPU time taken by the process等),这些数据会一直保留到系统将它传递给它的父进程为止,直到父进程通过wait / waitpid来取时才释放。
也就是说,当一个进程死亡时,它并不是完全的消失了。进程终止,它不再运行,但是还有一些残留的数据等待父进程收回。
当父进程 fork() 一个子进程后,它必须用 wait() (或者 waitpid())等待子进程退出。正是这个 wait() 动作来让子进程的残留数据消失。
但是如果父进程先于子进程结束了,则子进程会认init 进程为父进程。
一、什么是僵尸进程
僵尸进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。
当用ps命令观察进程的执行状态时,看到这些进程的状态栏为defunct。
二、僵尸进程的危害
调度程序无法选中Defunct 进程,所以不能用kill命令删除Defunct 进程
defunct进程占用系统的内存资源,影响系统的性能, 而且如果其数目太多,
将因为没有可用的进程号而导致系统不能产生新的进程。 导致系统瘫痪。
此时的错误为:
fork: retry: Resource temporarily unavailable
查看:
ps -ef <defunct> 太多
三、僵尸进程的产生
如果子进程死亡时父进程没有 wait(),通常用 ps 可以看到它被显示为“<defunct>”,这样就产生了僵尸进程。它将永远保持这样直到父进程 wait()。
由此可见,defunct进程的出现时间是在子进程终止后,但是父进程尚未读取这些数据之前。
四: 孤儿进程(orphan process)
但是如果该进程的父进程已经先结束了,那么这个进程就成为孤儿进程,那么该进程就不会变成僵尸进程。
因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看看有没有哪个进程是刚刚结束的这个进程的子进程,
如果是的话,就由Init进程来接管他,成为他的父进程,从而保证每个进程都会有一个父进程。而Init进程会自动wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程。
就是说,孤儿进程会由init 进程接管。
如果一个程序设计上有缺陷,就可能导致某个进程的父进程一直处于睡眠状态或是陷入死循环,父进程没有wait子进程,也没有终止以使Init接管,
该子进程执行结束后就变成了defunct进程,这个defunct 进程可能会一直留在系统中直到系统重新启动。
四、如何避免僵尸进程
a、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
b. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。
c. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,
那么子进程结束后,内核会回收,并不再给父进程发送信号
下面给出一个实例, 放开两处注释中的任一处,都不会产生僵尸进程。 用ps -ef |grep test 观察。
[hjj@hjj ~/test]$ cat test.cpp
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
// signal(SIGCLD, SIG_IGN);
int pid = fork ();
if (pid == 0)
{
printf("child exit now ...");
_exit (0);
}
else
{
/*
int i = 0;
int status = 0;
while (!waitpid(pid, &status, WNOHANG))
{
printf ("father waiting%d\n", ++i);
sleep (1);
}
*/
while (1)
{
printf ("father running ...\n");
sleep (1);
}
return 0;
}
}