僵尸进程以及如何避免

a.  fork: 子进程拷贝父进程的数据段,代码段,代码段和数据段虽然一样,但是独立拥有。
    vfork: 子进程与父进程共享数据段
b.  fork: 父子进程的执行次序不确定
    vfork: 保证子进程先运行,在调用exec或exit 之前与父进程数据是共享的,
           在它调用exec或exit之后父进程才可能被调度运行。
c.  vfork: 保证子进程先运行,在她调用exec或exit之后父进程才可能被调度运行。
    如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。

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

int main()
{
    int cnt = 0;
    pid_t pid = vfork();

    if (0 == pid)
    {
        ++cnt;
        printf("the cnt of child process: %d, pid: %d\n", cnt, getpid());
        /* exit(0); */
    }
    else if (pid > 0)
    {
        ++cnt;
        printf("the cnt of parent process: %d, pid: %d\n", cnt, getpid());
    }

    return 0;
}

hostname [4:27] [tmp/demo/vfork] -> gcc -o main process_demo.c
hostname [4:27] [tmp/demo/vfork] -> ./main
the cnt of child process: 1, pid: 15274
the cnt of parent process: 2, pid: 15273

注销exit后
hostname [4:27] [tmp/demo/vfork] -> gcc -o main process_demo.c
hostname [4:27] [tmp/demo/vfork] -> ./main
the cnt of child process: 1, pid: 17024
the cnt of parent process: -145322832, pid: 17023
Segmentation fault

将vfork改为fork
hostname  [4:27] [tmp/demo/vfork] -> gcc -o main process_demo.c
hostname [4:40] [tmp/demo/vfork] -> ./main
the cnt of parent process: 1, pid: 26587
the cnt of child process: 1, pid: 26591

在类UNIX系统中,僵尸进程是指完成执行(通过exit系统调用,或运行时发生致命错误或收到终止信号所致),
但并没有释放它的进程控制块PCB(其中存放进程ID、该进程的exit status、以及进程使用的CPU时间总量),
这些资源仍然保存在操作系统的进程表中。

ps aux命令将僵死进程的状态显示为Z

当一个子进程退出时,并不是立即释放其占用的资源(PCB),操作系统会发送SIGCHLD 信号给父进程,父进程对其默认处理是忽略。
如果想响应这个消息,父进程通常在SIGCHLD 信号事件处理程序中,使用wait系统调用来响应子进程的终止。

在这一过程中,操作系统将依次产生如下事件:
(1)向父进程发送SIGCLD信号,子进程进入zombie(僵尸)状态。
(2)父进程接收到SIGCLD信号,进行处理

进程长时间保持僵尸状态一般是错误的并导致资源泄漏。

孤儿进程是父进程已经退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。
孤儿进程将被init进程(PID为1)所收养,并由init进程对它们完成状态回收工作。

僵尸进程测试程序:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;

        printf("before fork\n");

        if ((pid = fork()) < 0)
                printf("fork error");
        else if (0 == pid) // child process
        {
                exit(0);
        }
        system("ps -o pid,ppid,state,tty,command");

        return 0;
}

output:
./main 
before fork
  PID  PPID S TT       COMMAND
17875 20420 S pts/19   ./main
17876 17875 Z pts/19   [main] <defunct>
17877 17875 R pts/19   ps -o pid,ppid,state,tty,command
20420 10909 S pts/19   -csh

如何避免僵尸进程
(1)父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起
(2)如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,在handler中调用wait回收
(3)如果父进程不关心子进程何时结束,一般在服务器程序中直接忽略SIGCHLD,用signal(SIGCHLD, SIG_IGN),子进程结束后,内核会回收子进程的PCB,不再给父进程发送信号
(4) fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收要由父进程做

方法(2)程序
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>

void handle(int sigid)
{
        // wait any process
        pid_t pid = waitpid(-1, NULL, 0);
        printf("wait %d process to exit\n", pid);
}

int main()
{
        pid_t pid;

        if (SIG_ERR == signal(SIGCHLD, handle))
        {
                printf("create signal handler error");
                exit(0);
        }

        if ((pid = fork()) < 0)
                printf("fork error");
        else if (0 == pid) // child process
        {
                exit(0);
        }

sleep(1);
        system("ps -o pid,ppid,state,tty,command");

        return 0;
}

output:
 ./main 
wait 30890 process to exit
  PID  PPID S TT       COMMAND
20420 10909 S pts/19   -csh
30889 20420 S pts/19   ./main
30891 30889 R pts/19   ps -o pid,ppid,state,tty,command
wait -1 process to exit

方法(4)程序
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;

        if ((pid = fork()) < 0)
                printf("fork son process error");
        if (0 == pid) // in son process
        {
                if ((pid = fork()) < 0)
                        printf("fork grandson process error");
                else if (pid > 0) // exit son process
                {
                        printf("son process, pid = %d, ppid= %d\n", (int)getpid(), (int)getppid());
                        exit(0);
                }

                // in the granson process
                sleep(2);
                system("ps -o pid,ppid,state,tty,command");
                exit(0);
        }

        printf("son process: %d\n", (int)pid);
        // wait son process terminate
        if (pid != waitpid(pid, NULL, 0))
        {
                printf("wait son process error");
                exit(-1);
        }

        printf("father process, pid = %d, ppid = %d\n", getpid(), getppid());
        printf("exit\n");

        exit(0);
}

output:
./main
son process: 19004
son process, pid = 19004, ppid= 19003
father process, pid = 19003, ppid = 20420
exit
$ PID  PPID S TT       COMMAND
19005     1 S pts/19   ./main
19006 19005 R pts/19   ps -o pid,ppid,state,tty,command
20420 10909 S pts/19   -csh








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值