Linux 多进程(三)进程控制,孤儿进程,僵尸进程

进程控制,处理僵尸进程(父进程如何等待子进程的退出)

exit 进程退出

#include<stdlib.h>
void exit(int status);//是标准c库函数
//内部调用系统调用
#include<unistd.h>
void _exit(int status);

image

#include<stdio.h>
#include<unistd.h>

int main()
{
    printf("hello\n");//这里有\n刷新缓冲区,将缓冲区的内容写出
    printf("world");//没\n刷新缓冲区,所以在_exit程序退出后,没有打印

    _exit(0);
    return 0;
}

image

而exit标准C函数会进行刷新IO缓冲,关闭文件描述符

孤儿进程

父进程结束,而子进程还在运行,这时此子进程就叫孤儿进程

内核会把孤儿进程的父进程设置为init进程,init会不断的wait它的子进程,孤儿进程的生命周期结束后由init进程来进行处理

init进程pid = 1

//使用sleep,让子进程后于父进程结束
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
    pid_t pid;
    pid = fork();
    if(pid>0){//parent
        printf("i am parent process,pid:%d,ppid:%d\n",getpid(),getppid());
    }else if(pid==0){
        sleep(1);
         printf("i am chiild process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    for(int i = 0;i<3;i++)
    {
        printf("i:%d\n",i);
    }
    return 0;
}

image

僵尸进程

对于多进程程序,父进程一般需要跟踪子进程的退出状态,所以子进程退出后,内核不会立即释放改进程的进程表表项,来让父进程可以对子进程的退出信息进行查询

僵尸态:(1)子进程结束,父进程读取其退出状态之前,子进程处于僵尸态

(2)父进程结束或异常终止,子进程继续运行,此时init进程接管该子进程,那么在父进程退出后,子进程退出前,该子进程处于僵尸态

如果父进程没有正确处理子进程的返回信心,子进程都将处于僵尸态,占据内核资源

内核区的PCB无法自己释放,需要父进程来释放

僵尸进程无法被kill -9发送的信号杀死

image

//产生一个僵尸进程
//让进程用while阻塞,无法获取子进程的退出状态
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
    pid_t pid;
    pid = fork();
    if(pid>0){//parent
        while(1){
            printf("i am parent process,pid:%d,ppid:%d\n",getpid(),getppid());
            sleep(1);
        }
    }else if(pid==0){
         printf("i am chiild process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    for(int i = 0;i<3;i++)
    {
        printf("i:%d\n",i);
    }
    return 0;
}

image

处理的一种方法:
结束父进程,使子进程被init进程托管

image

wait

父进程调用wait等待子进程结束

wait取得子进程结束时传给exit的值

#include <sys/types.h>
       #include <sys/wait.h>

       pid_t wait(int *status);
//即为调用:
	waitpid(-1,&status,0);
//此函数会回收子进程的资源
//返回pid,失败返回-1(调用的进程没有子进程也没有得到终止状态值(所有的子进程都结束且没有得到终止状态值)),设置errno
//默认是阻塞的
//参数为进程退出的状态
       pid_t  waitpid(pid_t  pid,  int  *status,   int
       options);
//功能与wait相同,可以设置否阻塞进程,可以指定要回收的进程的pid,

       The value of pid can be:

       < -1   meaning wait for any child process whose 回收abs(组id)的进程(某进程组的某个进程)
              process group ID is equal to  the  abso‐
              lute value of pid.

       -1     meaning wait for any child process. 回收所有子进程中的任一子进程

       0      meaning wait for any child process whose   回收当前进程组的任一子进程
              process group ID is equal to that of the
              calling process.

       > 0    meaning wait for the child whose process
              ID is equal to the value of pid.
           
       The value of options is an OR of zero  or  more //设置阻塞或非阻塞
       of the following constants:
		//0为阻塞
       WNOHANG     return  immediately if no child has
                   exited.			//如果没有了子进程,则立刻返回,非阻塞
	
       WUNTRACED   also return if a child has  stopped
                   (but  not  traced  via  ptrace(2)).
                   Status for  traced  children  which
                   have  stopped  is  provided even if
                   this option is not specified.	// 

       WCONTINUED (since Linux 2.6.10)
                   also return if a stopped child  has
                   been  resumed  by  delivery of SIG‐
                   CONT.
                       
//当option为WNOHANG时,如果pid指定的目标子进程还没有结束或意外结束,则waitpid返回0,如果目标子进程确实正常退出则返回该子进程的PID,如果调用失败返回-1

在事件已经发生的情况下执行非阻塞调用才能提高程序效率,所以waitpid最好在某个子进程确定退出之后再调用

我们使用SIGCHILD信号来让父进程得知某个子进程已经退出

一个进程结束时,会发送给其父进程发送SIGCHILD信号,因此:捕获此信号,在信号处理函数中调用waitpid函数

static void handle_child(int sig){
	pid_t pid;
    int stat;
    while((pid=waitpid(-1,&stat,WNOHANG))>0){//直到所有的子进程都处理结束,退出循环
		//善后处理
    }
}

父进程执行wait,内核挂起父进程直到子进程结束,直到它的一个子进程退出或者它收到一个不可忽略的信号,如果没有子进程存在了,函数会立刻返回-1,所有子进程都结束了,也会返回-1

一次只能应对一个子进程,所以多个子进程需要使用循环来处理

如果一个子进程被杀死或已经退出则对wait的调用立刻返回,wait返回结束进程的PID,如果status非NULL,则wait将退出状态或者信号序号浮直到其中(指向的整数),使用<sys/wait.h>中的宏检测

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
    pid_t pid;
    //创建五个子进程
    for(int i = 0;i<5;i++){
        pid = fork();
        if(pid==0)break;//防止子进程再创建子进程
    }
    if(pid>0){
        while(1){
            printf("parent pid:%d\n",getpid());
            sleep(1);    
        }
    }else if(pid==0){
        printf("child pid:%d\n",getpid());
    }//让子进程先死亡,得到五个僵尸进程
    return 0;
}

image

//调用wait,等待子进程退出,并且输出结束的子进程的id
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    pid_t pid;
    //创建五个子进程
    for(int i = 0;i<5;i++){
        pid = fork();
        if(pid==0)break;//防止子进程再创建子进程
    }
    if(pid>0){
        while(1){
            printf("parent pid:%d\n",getpid());
            int ret = wait(NULL);
            printf("child end ,pid = %d\n",ret);
            sleep(1);    
        }
    }else if(pid==0){
        printf("child pid:%d\n",getpid());
    }//让子进程先死亡,得到五个僵尸进程
    return 0;
}

image

使用宏来检测退出状态

(1)wait执行通知操作:

image

image

waitpid中的pid是指的是任意pid,而不是所有,waitpid一次只回收一个进程的资源

//尝试使用waitpid函数
//首先是非阻塞情况:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    pid_t pid;
    //创建五个子进程
    for(int i = 0;i<5;i++){
        pid = fork();
        if(pid==0)break;//防止子进程再创建子进程
    }
    if(pid>0){
        while(1){
            printf("parent pid:%d\n",getpid());
            int stat;
            int ret = waitpid(-1,&stat,0);//设置为阻塞,如果没有子进程结束,则下面的代码都没有执行
            if(ret==-1)break;
            //使用kill命令结束子进程,父进程就会恢复为非阻塞,执行下列代码
            if(WIFEXITED(stat)){
                //正常退出
                printf("正常退出的状态码:%d\n",WEXITSTATUS(stat));
            }
            if(WIFSIGNALED(stat)){
                //异常终止
                printf("被%d信号终止\n",WTERMSIG(stat));
            }
            printf("child end ,pid = %d\n",ret);
            sleep(1);    
        }
    }else if(pid==0){
        while(1){
            printf("child pid:%d\n",getpid());
            sleep(1);
        }
            
    }
    return 0;
}

image
阻塞情况:

//只用更改一行调用waitpid的代码
int ret = waitpid(-1,&stat,WNOHANG);

image
因为return 0所以退出的状态码为0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值