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