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);
。。。。。。