菜鸟随笔(3)---三种进程学习.孤儿进程.僵尸进程.守护进程

一只菜鸟横空出世,码农世界闯一闯,每天进展多一丢丢。

  

                        三种进程学习.孤儿进程.僵尸进程.守护进程

转自https://blog.csdn.net/believe_s/article/details/77040494

1、孤儿进程

如果父进程先退出,子进程还没退出那么子进程将被 托孤给init进程,这里子进程的父进程就是init进程(1号进程).其实还是很好理解的。

// 父进程先子进程退出
// 子进程会被祖父进程接手并在后台运行,执行内部的代码
int main()
{
    pid_t pid = fork();

    switch (pid)
    {
        case -1:
            perror ("fork");
            break;
        case 0:             // 子进程
            close (1);

            // 创建一个文件用来保存输出的文字
            int fd = open ("child", O_RDWR|O_CREAT, 0777);
            printf ("我是子进程,我的ID是%d
", getpid());
            while (1)
            {
                printf ("找爸爸
");
                fflush (stdout);
                sleep (2);
            }
            break;
        default:            // 父进程
            printf ("我是父进程:ID = %d
", getpid());
            printf ("我走啦
");
            while (1);
            break;
    }

    return 0;
}

菜鸟随笔(3)---三种进程学习.孤儿进程.僵尸进程.守护进程

菜鸟随笔(3)---三种进程学习.孤儿进程.僵尸进程.守护进程

菜鸟随笔(3)---三种进程学习.孤儿进程.僵尸进程.守护进程 
在这里我们运行了程序,可以在另一终端看到有2个a.out在运行,我们将父进程终止,子进程并没有退出,而是在后台继续运行,并向child文件中输出文字。

2、僵尸进程 
菜鸟随笔(3)---三种进程学习.孤儿进程.僵尸进程.守护进程 
如果我们了解过Linux进程状态及转换关系,我们应该知道进程这么多状态中有一种状态是僵死状态,就是进程终止后进入僵死状态(zombie),等待告知父进程自己终止,后才能完全消失.但是如果一个进程已经终止了,但是其父进程还没有获取其状态,那么这个进程就称之为僵尸进程.僵尸进程还会消耗一定的系统资源,并且还保留一些概要信息供父进程查询子进程的状态可以提供父进程想要的信息.一旦父进程得到想要的信息,僵尸进程就会结束.

// 子进程比父进程先退出
int main()
{
    int count = 5;
    while (count--)
    {
        //signal(SIGCHLD,SIG_IGN);
        pid_t pid = fork();

        switch (pid)
        {
            case -1:
                perror ("fork");
                break;
            case 0:         // 子进程
                printf ("我是子进程,我的ID是%d
", getpid());
                printf ("我走啦
");
                exit (0);
            default:        // 父进程
                printf ("我是父进程,我的ID是%d
", getpid());
                //while(1);
                break;
        }
    }
    while(1);

    return 0;
}

菜鸟随笔(3)---三种进程学习.孤儿进程.僵尸进程.守护进程

signal(SIGCHLD,SIG_IGN);加上这行代码后僵尸进程都消失了。 
菜鸟随笔(3)---三种进程学习.孤儿进程.僵尸进程.守护进程 
通过ps -ef | grep a.out 我们可以得知进程信息和进程pid,可以看到子进程就是处于defunct状态.这时我们肯定想要怎么才能避免僵尸进程呢?看程序被我注释的那句signal(SIGCHLD,SIG_IGN),加上就不会出现僵尸进程了.那我们就加点篇幅讲一下为什么就可以避免僵尸进程呢? 
这是signal()函数的声明sighandler_t signal(int signum, sighandler_t handler),我们可以得出,signal函数的第一个函数是linux支持的信号,第二个参数是对信号的操作 ,是系统默认还是忽略或捕获.我们这是就可以知道signal(SIGCHLD,SIG_IGN)是选择对子程序终止信号选择忽略,这是僵尸进程就是交个内核自己处理,并不会产生僵尸进程.

3、守护进程 
同样我们需要了解一下什么是守护进程,守护进程就是在后台运行,不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,它们以root用户或者其他特殊用户(apache和postfix)运行,并能处理一些系统级的任务.习惯上守护进程的名字通常以d结尾(sshd),但这些不是必须的.

下面介绍一下创建守护进程的步骤:

· 调用fork(),创建新进程,它会是将来的守护进程. 
· 在父进程中调用exit,保证子进程不是进程组长 
· 调用setsid()创建新的会话区 
· 将当前目录改成跟目录(如果把当前目录作为守护进程的目录,当前 目录不能被卸载他作为守护进程的工作目录) 

// 守护进程
int daemonize (int nochdir, int noclose)
{
    // 创建子进程,关闭父进程
    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;
    }

    // 当nochdir为0时,daemon将更改进程的根目录为root
    if (0 == nochdir)
    {
        // 改变当前的工作目录
        if (chdir ("/") < 0)
        {
            return -1;
        }
    }

    // 标准输入、关闭标准输出、标准错误
    close (STDIN_FILENO);
    close (STDOUT_FILENO);
    close (STDERR_FILENO);

    if (0 == noclose)
    {
        // 重定向标准输入、关闭标准输出、标准错误
        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;
}