您的位置:首页 > 其它

僵尸进程以及如何避免

2015-02-11 09:42 513 查看
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


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: