进程控制3—父子进程の恩怨情仇

子进程比父进程先退出:僵尸进程
僵尸进程指的是那些虽然已经终止的进程,但仍然保留一些信息,等待其父进程为其收尸。
如何产生?
如果一个进程在其终止的时候,自己就回收所有分配给它的资源,系统就不会产生所谓的僵尸进程了

僵尸进程产生的过程:
1. 父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。

  1. 子进程的状态变成EXIT_ZOMBIE,并且向父进程发送SIGCHLD 信号,父进程此时应该调用 wait() 系统调用来获取子进程的退出状态以及其它的信息。在 wait 调用之后,僵尸进程就完全从内存中移除。

  2. 因此一个僵尸存在于其终止到父进程调用 wait 等函数这个时间的间隙,一般很快就消失,但如果编程不合理,父进程从不调用 wait 等系统调用来收集僵尸进程,那么这些进程会一直存在内存中。

子进程先父进程退出

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


//子进程先父进程退出
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://父进程若是不处理子进程,会产生僵尸进程
                //printf ("我是父进程,id = %d\n", getpid());
                //while(1);
                break;
        }
    }

    while(1);
    return 0;
}

在终端上运行后用ps -ef | grep a.out抓取进程,发现
这里写图片描述
多了十个僵尸进程,这就是父进程不处理子进程的后果,使用
killall a.out命令可以强制退出。

父进程先子进程退出
若父进程比子进程先终止,则该父进程的所有子进程的父进程都改变为init进程。我们称这些进程由init进程领养。其执行顺序大致如下:在一个进程终止时,内核逐个检查所有活动进程,以判断它是否是正要终止的进程的子进程,如果是,则该进程的父进程ID就更改为1(init进程的ID);

有init领养的进程不会称为僵死进程,因为只要init的子进程终止,init就会调用一个wait函数取得其终止状态。这样也就防止了在系统中有很多僵死进程。

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

//父进程先子进程退出
int main()
{
    pid_t pid = fork();

    switch (pid)
    {
        case -1:
            perror ("fork");
            break;
        case 0:     //子进程
            printf ("我是子进程,id = %d\n", getpid());
            printf ("我走啦\n");
            while(1)
            {
                printf ("找爸爸\n");
                fflush (stdout);
                sleep(2);
            }
            break;
        default:    //父进程
            //printf ("我是父进程,id = %d\n", getpid());
            //while(1);
            exit(0);
            break;
    }
    return 0;
}

这段代码执行后就让子进程一直跑在后台,不断地往屏幕输出“找爸爸”,需要在另一个终端上关掉它
这里写图片描述
但是这个有很大的用处的,子进程跑在后台多么像我们的软件跑在后台,你是不是发现了什么。
这里我们就可以创建一个守护进程:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.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;

}

int main()
{
    //daemonize(0,0);
    daemon (0,0);
    while (1);
    return 0;
}

关于父子进程的等待

wait()

#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options); 
//返回值:若成功返回进程ID,若出错返回-1

调用wait或waitpid的进程可能发生的情况有:
如果所有子进程都还在运行,则阻塞(Block)。
如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。
如果它没有任何子进程,则立即出错返回。

在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞。
waitpid并不等待在其调用之后的第一个终止的子进程。它有若干个选项,可以控制它所等待的进程。
如果一个子进程已经终止,并且是一个僵死进程,wait立即返回并取得该子进程的状态,否则wait使其调用者阻塞直到一个子进程终止。如果调用者阻塞并且它有多个子进程,则在其一个子进程终止时,wait就立即返回。因为wait返回终止子进程的ID,所以总能了解到是哪一个子进程终止了。

注:僵死进程(zombie),一个已经终止、但是其父进程尚未对其进行善后处理(获得终止子进程的有关信息,释放它仍占用的资)的进程被称为僵死进程。

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

waitpid

#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid (pid_t pid, int * status, int options)
//功能:会暂时停止目前进程的执行,直到有信号来到或子进程结束

参数:如果不在意结束状态值,则参数status可以设成NULL。
参数pid为欲等待的子进程识别码:
pid<-1 等待进程组识别码为pid绝对值的任何子进程。
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid的子进程。

参数option可以为0 或下面的OR 组合
WNOHANG: 如果没有任何已经结束的子进程则马上返回,不予以等待。
WUNTRACED :如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。

返回值:如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值