Linux C :系统调用-fork,wait,subreaper

     fork():创建子进程,并返回进程id。

     wait(&status):等待子进程终止。如果成功则会返回僵尸子进程的pid,status的值会是子进程的exitcode。

     exit(value):进程正常退出,并返回退出值value

     prctl(PR_SET_CHILD_SUBREAPER):设置当前进程为subreaper进程。

零、示例代码即可能的输出结果

目录

一、fork()

二、wait()

三、subreaper

四、解读上述代码和输出结果


#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <sys/prctl.h>
int main(){
    int pid ,r,status ;
    printf("mark process %d as a subreaper\n",getpid());
    r=prctl(PR_SET_CHILD_SUBREAPER);
    pid = fork();
    if(pid){
        //parent 
        printf("subreaper %d child =%d\n",getpid(),pid);
        while(1){
	        pid =wait(&status);
	        if(pid >0){
	            printf("subreaper %d wait a ZOMBIE=%d\n",getpid(),pid);
	        }else{
	            break;
	        }
        }
   }else{
        //child
        printf("child %d parent=%d\n",getpid(),(pid_t)getppid());
        pid=fork();
        if(pid){
	        printf("child%d start : grandchild=%d\n",getpid(),pid);
	        printf("child%d exit : grandchild=%d\n",getpid(),pid);
        }else{
            printf("grandchild%d start : myparent=%d\n",getpid(),getppid());
	        printf("grandchild%d end : myparent=%d\n",getpid(),getppid());
        }
     }

}

运行结果:(每次运行可能顺序不一样)

      在Linux 中,每个用户在同一时间只能有一定数量的进程。用户资源限制文件通常在/etc/security/limits.conf 文件中设置。进程有两种执行状态,一种是内核态,一种是用户态。每个进程都在内核中下产生并开始执行,想调用硬件设备例如操作CPU,键盘,或者想执行系统调用都是在内核态下完成的。修改状态寄存器的值就可以从内核态转化为用户态。一旦转为用户态,那么就无法直接访问硬件相关的设备,想要重新转为内核态只有3种方式:硬件中断,异常,系统调用。 可以说,内核是操作系统之下的部分,用户是操作系统之上的部分。用户只需关心操作系统为用户提供了哪些可访问内核的接口就行。

      在内核模式中,每个进程都有完全相同的代码段,数据段和堆,但是每个进程都有只属于自己的栈。

一、fork()

     创建子进程并返回子进程的pid , int pid = fork();   如果fork失败则为-1。当创建了子进程后,子进程和父进程在用户态的映像完全相同,但是在内核态下的映像只有栈有区别,意味着子进程和父进程有相同的代码和数据。因为创建子进程时用户态的栈也是一样的,意味着栈帧情况也是一样的。创建完后子进程和父进程都从fork()后继续执行.fork()方法调用了内核中kfork()函数,它对创建出来的子进程返回是0,父进程返回值是子进程的进程号;

int pid =fork();
if(pid=-1){
   //父进程创建子进程失败的分支
}else if(pid ==0){
   //子进程执行的分支,因为子进程返回的pid是0
}else {
   //父进程执行的分支,pid是子进程的id
}

二、wait()

       等待僵尸子进程。所谓的僵尸进程,就是程序已经结束但是资源未释放的进程。wait除了返回僵尸子进程的id,获取子进程的退出状态,还会释放僵尸子进程。wait系统调用将调用内核中的kwait函数

三、subreaper

进程可以用系统调用定义自己为subreaper进程:

prctl(PR_SET_CHILD_SUBREAPER);

      如果子进程的父进程先死亡,那么该子进程就会成为一个孤儿进程。在Linux中,标记为subreaper的祖先进程会成为该孤儿的父进程,否则将成为P1进程子进程。(P1进程是操作系统初始化的进程,除P0和P1进程,其他所有进程都是P1进程的子孙进程)。不过要注意的是,当P1清理重定父级进程时,会丢失关于子进程的所有信息。P1因为进程等级高,当P1作为孤儿进程的父进程时服务管理器不能再从服务守护进程中接收SIGCHLD信号,也不能等待任何僵尸子进程。定义subreaper就可以很好的解决上述问题,并且可以有效地减少P1进程的工作量。

显示当前用户的subreaper 进程的PID 和信息:

     ps fxau | grep USERNAME | grep "/sbin/upstart"

四、解读上述代码和输出结果

  就运行结果而言,

1)当fork创建出来子进程后,父进程获取到CPU使用权先执行,并打印出了

      

      然后进入while循环等待子进程执行结束。

2)接下来子进程执行并打印出了,

      

     之后子进程 fork 创建出了一个子进程的子进程即孙子进程。

3)子进程先执行完毕,并打印出:

      

      此时,子进程执行完毕但是未释放进程资源,于是成为了僵尸进程。

4)此时父进程获取到CPU使用权,通过wait返回子进程的pid,继续执行打印出了  

     

     wait释放了子进程的资源,使孙子进程成为了孤儿进程,但是由于之前父进程(pid=3434)将自己设置成了PR_SET_CHILD_SUBREAPER  , 使 孙子进程(pid=3436)的父亲不再是已经死亡的(pid=3435),而是父进程(pid=3434)。

5)所以当孙子执行完自己的代码后,打印出

     成为了僵尸进程后,被父进程 wait() 释放出来,打印出

    后结束。

猜你喜欢

转载自blog.csdn.net/superSmart_Dong/article/details/118372165