僵尸进程与孤儿进程

1、前言

  前几天看《TCP&IP网络编程》时候看到了相关知识,为了加深印象,在捋一遍。

2、基础概念

  在unix/linux中,正常情况下,子进程是通过父进程fork()创建的,子进程再创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。 当一个进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。

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

僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵进程。

调用fork函数产生子进程的终止方式:

1)传递参数并调用exit函数;

2)main函数中执行return语句并返回值。

  向exit函数传递的参数值和main函数的return语句返回的值都会传递给操作系统。而操作系统不会销毁子进程,直到把这些值传递给产生该子进程的父进程,处在这种状态下的进程就是僵尸进程。也就是说,将子进程变成僵尸进程的正是操作系统。既然如此,此僵尸进程何时被销毁呢?

  “应该向创建子进程的父进程传递子进程的exit参数值或者return语句的返回值。”

  如何向父进程传递这些值呢?操作系统不会主动把这些值传递给父进程。只有父进程主动发起请求(函数调用)时,操作系统才会传递该值。换言之,如果父进程未主动要求获得子进程的结束状态值,操作系统将一直保存,并让子进程长时间处于僵尸进程状态。也就是说,父母要负责收回自己的孩子。

3、销毁僵尸进程

  一般,为了防止产生僵尸进程,在fork子进程之后我们都要wait它们;同时,当子进程退出的时候,内核都会给父进程一个SIGCHLD信号,所以我们可以建立一个捕获SIGCHLD信号的信号处理函数,在函数体中调用wait或waitpid,就可以清理退出的子进程以达到防止僵尸进程的目的。

相关函数介绍:

1)wait函数

            

  调用此函数时如果已有子进程终止,那么子进程终止时传递的返回值(exit函数的参数值、main函数的return返回值)将保存到该函数的参数所指内存空间。但函数参数中还包含其他信息,因此需要通过下列宏进行分离。

  • WIFEXITED子进程正常终止时返回“真”(true)
  • WEXITSTATUS返回子进程的返回值

  也就是说向wait函数传递变量status的地址时,调用wait函数后应编写如下代码:

if(WIFEXITED(status))

{

  puts("normal termination!");

  printf("child pass num:%d ",WEXITSTATUS(status));

}

调用wait函数时,如果没有已终止的子进程,那么程序将阻塞直到有子进程终止,因此需谨慎调用该函数。

2)waitpid函数

  wait函数会引起程序阻塞,还可以考虑调用waitpid函数。这是防止僵尸进程的第二种方法,也是防止阻塞的方法。

          

 3)signal函数

        

  函数名:signal

  参数:int signo,void(* func)(int)

  返回类型:参数为int型,返回void型函数指针

  调用上述函数时,第一个参数为特殊情况信息,第二个参数为特殊情况下将要调用的函数的地址值(指针)。发生第一个参数代表的情况时,调用第二个参数所指的函数。下面是可以在signal函数中注册的部分特殊情况和对应的函数。

  • SIGALRM:已到通过调用alarm函数注册的时间
  • SIGINT:输入Ctrl+C
  • SIGCHLD:子进程终止

eg:signal(SIGCHLD, mychild)

  signal(SIGALRM, alarm)

  signal(SIGINT, keycontrol) 

 4)sigaction函数

signal函数在Unix系列的不同操作系统中可能存在区别,但sigaction完全不会。可以代替signal。

      

  此结构体的sa_handler成员保存信号处理函数的指针值,sa_mask和sa_flags的值全初始化为0即可。这两个成员用于指定信号相关的选项和特性。

eg1:

  struct sigaction act;

  act.sa_handler=timeout;

  sigemptyset(&act.sa_mask);

  act.sa_flags=0;

  sigaction(SIGALRM,&act,0);

eg2 利用信号处理技术消灭僵尸进程(子进程终止时将产生SIGCHLD信号)

  struct sigaction act; 

  act.sa_handler=child_proc;

  sigemptyset(&act.sa_mask);

  act.sa_flags=0;

  sigaction(SIGCHLD,&act,0);

参考:https://www.cnblogs.com/Anker/p/3271773.html

  

原文地址:https://www.cnblogs.com/xingguang1130/p/12701726.html