fork wait 孤儿与僵尸

1- fork and wait

例子1:
父进程
----》 子进程1
----》 子进程2

#include 
#include 
#include 

int main(void)
{
        pid_t pid_1, pid_2;

        if(0 == (pid_1 = fork())) {
                printf("I'm child1. my pid = %d, pid_father = %d\n", getpid(), getppid());
        } else {
                if(0 == (pid_2 = fork())) {
                        printf("I'm child2. my pid = %d, pid_father = %d\n", getpid(), getppid());
                } else {
                        printf("I'm father. my pid = %d, first wait = %d, second wait = %d, pid_1 = %d, pid_2 = %d\n", getpid(), wait(NULL), wait(NULL), pid_1, pid_2);
                }
        }

        return 0;
}

可以看到,两个子进程具有相同的父进程,而父进程的两次wait()分别得到了两个子进程的pid。
fork()在子进程中返回0,而在父进程中返回子进程的pid。pid_1和pid_2的值说明了这点

执行结果:

I'm child1. my pid = 17507, pid_father = 17506
I'm child2. my pid = 17508, pid_father = 17506
I'm father. my pid = 17506, first wait = 17508, second wait = 17507, pid_1 = 17507, pid_2 = 17508

例子2: 共享内存, 子子进程11 与父进程通信
父进程
----》 子进程1----》 子子进程11

#include 
#include 
#include 
#include 
#include 

int main(void)
{
        int shmid = 0;
        int *shmbuf = NULL;

        if((shmid = shmget(777, sizeof(int), 0666 | IPC_CREAT)) < 0) {
                perror("shmget");
                exit(-1);
        }
        if((shmbuf = shmat(shmid, NULL, 0)) < 0) {
                perror("shmat");
                exit(-1);
        }

        if(0 == fork()) {
                if(0 == fork()) {
                        printf("I'm the child of child1. my pid = %d, pid_father = %d\n", getpid(), getppid());
                        *shmbuf = getpid();
                } else {
                        printf("I'm child1. my pid = %d, pid_father = %d, pid_my_child = %d\n", getpid(), getppid(), wait(NULL));
                }
        } else {
                printf("I'm father. my pid = %d, pid_my_child = %d, ", getpid(), wait(NULL));
                printf("pid_child_of_child1 = %d\n", *shmbuf);

                if(shmdt(shmbuf) < 0) {
                        perror("shmdt");
                        exit(-1);
                }
                if(shmctl(shmid, IPC_RMID, NULL) < 0) {
                        perror("shmctl");
                        exit(-1);
                }

        }

        return 0;
}

结果:

I'm the child of child1. my pid = 17521, pid_father = 17520
I'm child1. my pid = 17520, pid_father = 17519, pid_my_child = 17521
I'm father. my pid = 17519, pid_my_child = 17520, pid_child_of_child1 = 17521

例子3

#include 
#include 
int wait(int *status)

函数功能是:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

1,WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。

2, WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status) 就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char * argv[])
{

  int count = 0;
  pid_t pid = fork();
  int status = -1;
  
  if(pid<0)
  {
    printf("fork error for %m\n",errno );
  }else if(pid>0)
  {
    printf("this is parent ,pid = %d\n",getpid() );
    wait(&status);//父进程执行到此,马上阻塞自己,直到有子进程结束。当发现有子进程结束时,就会回收它的资源。
    
  }else
  {
    printf("this is child , pid = %d , ppid = %d\n",getpid(),getppid() );
    int i;
    
    for (i = 0; i < 10; i++) {
      count++;
      sleep(1);
      printf("count = %d\n", count)  ;
      
    }
    exit(5);
    
  }
  printf("child exit status is %d\n", WEXITSTATUS(status));//status是按位存储的状态信息,需要调用相应的宏来还原一下
  printf("end of program from pid = %d\n",getpid() );
}

运行结果:

this is parent ,pid = 17576
this is child , pid = 17577 , ppid = 17576
count = 1
count = 2
count = 3
count = 4
count = 5
count = 6
count = 7
count = 8
count = 9
count = 10
child exit status is 5
end of program from pid = 17576

2- 孤儿与僵尸

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

僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

2.1问题及危害
僵尸进程:
如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。

孤儿进程:
孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上, init循环回收, 因此孤儿进程并不会有什么危害。

任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个 子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。

2.2-僵尸进程解决办法
子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号

#include 
#include 
#include 
#include 
#include 

static void sig_child(int signo);

int main()
{
    pid_t pid;
    //创建捕捉子进程退出信号
    signal(SIGCHLD,sig_child);
    pid = fork();
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    else if (pid == 0)
    {
        printf("I am child process,pid id %d.I am exiting.\n",getpid());
        exit(0);
    }
    printf("I am father process.I will sleep two seconds\n");
    //等待子进程先退出
    sleep(2);
    //输出进程信息
    system("ps -o pid,ppid,state,tty,command");
    printf("father process is exiting.\n");
    return 0;
}

static void sig_child(int signo)
{
     pid_t        pid;
     int        stat;
     //处理僵尸进程
     while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
            printf("child %d terminated.\n", pid);
}

REF:https://blog.csdn.net/Eunice_fan1207/article/details/81387417

你可能感兴趣的:(fork wait 孤儿与僵尸)