琐记8:恐怖的僵尸进程与温暖的守护进程

1、被我标题吸引进来的同学先不要着急,在讨论标题中的内容之前,我们需要先了解下进程的终止:

exit 与_exit 函数都是用来结束进程的函数,首先是exit:它的头文件:<stdlib.h> ,其次它用来正常结束当前进程,并把参数status返回给父进程,而进程所有的缓冲区数据也会自动写回并关闭未关闭的文件。而_exit 函数的头文件为:<unistd.h>,该函数会在结束进程时清空缓冲区。


2、僵尸进程:当一个进程的子进程挂了,父进程没有为它处理后事,那么这个子进程就会永远占用着进程描述符(ID),成为一个僵尸进程。

作为实例,我们来看一下这样一个程序:

int main()
{ 
	int count = 10;
	while (count--)
	{
		pid_t pid = fork();	
		switch (pid)
		{
			case -1:
				perror ("fork");
				break;
			case 0:                 // 子进程
				printf ("我是子进程,我的Id 是%d\n", getpid());
				printf ("我走啦\n");
				exit(0);	// 子进程退出
			default: 		// 父进程啥事也不干
				break;
		}
	}
	while(1);	// 父进程不退出

	return 0;
}

我们来看一下这样的一个程序运行之后,在" ps -ef | grep a.out " 之后我们能得到怎样的一个结果:


我们可以看到,在那10个a.out 的后面跟了“defunct”这样一个单词,笔者翻过词典发现,该单词的意思是“死者”,“死去的”。对,这就是10具尸体,然而它们却依旧跑在后台,想想都很恐怖啊。

那怎么将它们处理掉呢,这时候就需要它们的父进程来执行一下安葬函数了:

3、安葬函数:wait 和 waitpid

1)按照用户手册上,wait() 函数的参数应该是"int * status"这样的指针,但我们可以省略,用NULL也行。如果一个子进程已经终止,并且是一个僵死进程,wait立即返回并取得该子进程的状态,否则wait使其调用者阻塞直到一个子进程终止。如果调用者阻塞并且它有多个子进程,则在其一个子进程终止时,wait就立即返回。因为wait返回终止子进程的ID,所以总能了解到是哪一个子进程终止了。

2)waitpid() 函数的参数就很多样化,pid_t  waitpid (pid_t  pid,  int *status,  int  options) ,首先,是子进程识别码,我们可以填上之前fork() 函数的返回值即是子进程的ID,如果子进程太多,可以选用-1或0,-1则和wait() 相同,任何子进程都可以,0是等待与目前进程相同的任何子进程。然后,我们来看参数option,可以为0 或下面的OR 组合:
WNOHANG:      如果没有任何已经结束的子进程则马上返回,不予以等待。
WUNTRACED :如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。

3)最后我们来看一下它们的返回值和共同参数" int *status",关于它的返回值,成功则返回子程序的ID,出错则返回-1。另外的参数,需要我们传入一个地址,如果子进程正常结束,则参数的值变成0,但如果是被干掉的,则会变为15。当然,我们也可以利用这样一些宏:

WIFEXITED(status)
    若子进程正常终止,该宏返回true。
    此时,可以通过WEXITSTATUS(status)获取子进程的退出状态(exit status)。
 WIFSIGNALED(status)
    若子进程由信号杀死,该宏返回true。
    此时,可以通过WTERMSIG(status)获取使子进程终止的信号值。
 WIFSTOPPED(status)
    若子进程被信号暂停(stopped),该宏返回true。
    此时,可以通过WSTOPSIG(status)获取使子进程暂停的信号值。
 WIFCONTINUED(status)
    若子进程通过SIGCONT恢复,该宏返回true。




4、守护进程:

当父进程挂掉,子进程会被祖先进程(init)接收运行在后台,成为一个守护进程。库函数里提供了这样一个守护进程的创建函数:deamon() ,但这边,我们来手动创建一个:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>

int daemonize(int nochdir, int noclose)
{
	// 1、创建子进程,关闭父进程
	pid_t pid = fork();
	if (pid > 0)
	{
		exit(0);
	}
	else if (pid < 0)
	{
		return -1;
	}
	
	// 2、设置文件的掩码, mode & ~umask
	umask(0);
	
	// 3、设置新的会话: 脱离当前会话和终端的控制
	if (setsid() < 0)
	{
		return -1;
	}
	
	if (nochdir == 0 )
	{
		// 4、改变当前的工作目录
		if (chdir("/") < 0)
		{
			return -1;
	}
	}
	
	// 标准输入、关闭标准输出、标准错误
	close(STDIN_FILENO);
	close(STDOUT_FILENO);
	close(STDERR_FILENO);
	
	if (noclose == 0)
	{
		// 重定向标准输入、关闭标准输出、标准错误
		open("/dev/null", O_RDONLY);   // 0 
		open("/dev/null", O_RDWR);   // 1
		open("/dev/null", O_RDWR);   // 2
	}

	return 0;
}
当然,上述创建方法完全可以由这样一句代码代替:
	daemon(0,0);

。。。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值