Linux 进程等待

目录

​编辑​编辑

小实验     子进程的退出码

第二个实验  手动退出

小结

WIFEXITED    WEXITSTAUS

非阻塞轮巡/非阻塞等待

非阻塞等待案例

等待失败的情况

非阻塞等待实验


Linux 进程等待_第1张图片Linux 进程等待_第2张图片

在2号手册里查wait()。wait()等待任意一个子进程的状态。

wait()等待成功会返回该子进程的id,返回失败会返回-1:

小实验     子进程的退出码

子进程执行work(),父进程wait子进程。

子进程跑完5秒之后就exit(0)退出。

子进程跑的时候是run状态

当子进程跑完,父进程接收到子进程的状态,即僵尸状态,然后父进程会跑10秒,10秒过后父进程也结束进程。

代码如下:

#include
#include
#include
#include
#include


void work()
{
    int cnt=5;    
    while(cnt--)    
    {    
    
    printf("子进程开始启动:pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);    
    sleep(1);    
    }    
}    
    
    
int main()    
{    
  
    sleep(10);    
    pid_t id=fork();    
   if(id==0)      //子进程
   {    
    
    work();                                                                                                                                        
    exit(0);    
   }    
   else //父进程
 {
    pid_t ret=wait(NULL);    
    if(ret==id)    
    {
        printf("wait success,pid:%d\n",getpid());
    }
}

    sleep(10);
    return 0;

 }
~

结论:子进程在跑,父进程就要进行阻塞等待。等子进程跑完,父进程才wait sucess,总结如下:

waitpid()

用法:

 三个参数,重点讲一下  stauts

status是输出型参数,所谓输出型参数需要用户自己定义变量,然后传参,操作系统接收这个参数经过操作之后再返回给用户级变量

如下,定义一个int型变量,变量名为status,取地址传参,然后等如果父进程等待子进程之后就把status的值打印出来看看:

结果如下:

status是一个整形,有32位,分为高八位和低八位:Linux 进程等待_第3张图片

 如果进程正常退出,就返回低八位的0,高八位的退出状态。

如果异常退出,就返回低七位的终止信号,为什么不是低八位呢? 低八位的第一位是core dump标志,所以只返回低七位。

如下,我们把子进程的退出码改为exit(10),再让父进程去等待:
Linux 进程等待_第4张图片运行结果:status为2560。

因为退出码10的二进制为1010,又因为异常所以在高八位,如下:

Linux 进程等待_第5张图片转化为十进制就是2560;








exit sign为stautus的信号退出码,exit code为status的进程退出码:

printf("wait success,pid:%d,status返回码:%d, 

exit sign:%d    ,exit code:%d\n",getpid(),ret,

status&0x7F,(status>>8)&0xFF);      

为什么exit sign是 status&0x7F?

因为信号退出码在低七位  7是3个1,F是4个1,合起来就是7个1,&7个1就把低七位保留,其他位全变0

同理,exit code为高八位  

运行结果:

Linux 进程等待_第6张图片退出信号为0,退出码为10、

如果我们把子进程的退出码改为正常退出码,即0,再跑,就会退出信号为0,退出码为0:

Linux 进程等待_第7张图片

我们给子进程写一个除0错误:Linux 进程等待_第8张图片

 退出信号会显示8:

我们让子进程出现空指针错误:

Linux 进程等待_第9张图片

退出信号为11:11就是段错误:

Linux 进程等待_第10张图片

第二个实验  手动退出

我们让子进程不要退出了,一直运行:Linux 进程等待_第11张图片

此时运行之后子进程就会一直跑,然后我们输入kill -9 pid终止进程,进程退出信号会显示9:Linux 进程等待_第12张图片

小结

Linux 进程等待_第13张图片父进程得到子进程的退出结果实际上是调用stautus这个整型变量,可以用一个指针解引用即可得到status。

WIFEXITED    WEXITSTAUS

Linux 进程等待_第14张图片

 用这两个宏就我们就可以不用关注返回值,宏会自己获取子进程的返回值。

调用WIFEXITED获取是否正常退出,如果为假,直接输出else里面的异常。

如果为真,就调用WEXITSTAUS获取子进程的退出码并打印出来。

Linux 进程等待_第15张图片

我们可以先搞一个异常出来,把子进程死循环:Linux 进程等待_第16张图片

运行:

我们再把子进程改为正常的再运行:

非阻塞轮巡/非阻塞等待

与阻塞式等待相对。

阻塞式等待父进程什么事情也不干,就在那等着子进程返回值。

阻塞式等待可以干自己的事情,等子进程返回的时候接收一下就可以了。

非阻塞等待案例

fork()函数创建一个子进程,子进程在循环中调用work()函数并逐秒递减计数器。当计数器达到0时,子进程通过exit(0)结束。父进程则使用waitpid()函数等待子进程的结束,并打印相关信息。子进程会运行10次work()函数然后退出。

#include 
#include
#include
#include
#include

    void work(int cnt)
    {
       printf("i am child process ,pid:%d,cnt:%d\n",getpid(),cnt);
    }
int main()
{

//    int status=0;


    //子进程
    pid_t id=fork();

    if(id==0)//说明子进程创建成功
    {

    
   int cnt=10;
    while(cnt )
    {
      //  printf("child process creat sucess\n");
        work(cnt);
        sleep(1);
        cnt--;
    }
    exit(0);
    
    }
    //父进程
    int status=0;
    pid_t rid=waitpid(id,&status,WNOHANG);

    if(rid>0)
    {
      //走到这里,说明父进程等待子进程成功
      printf("child quit sucess,exit code:%d,exit sigin:%d\n ",(status<<8)&0xFF,status&0x7F);
    }

    else if(rid==0)
    {
        //走到这里,父进程等子进程成功,但是子进程还未退出。
        printf("father to do other things\n");
    }
    else 
    {
        //走到这里就只有最后一种情况:rid<0,说明有异常,一般都是pid写错了
        printf("wait fail\n");
    }

return 0;
}

等待失败的情况

pid写错就会等待失败:

Linux 进程等待_第17张图片

这里故意写错。

那么父亲节就检测到子进程不是它的,父进程就退了,子进程还在继续

Linux 进程等待_第18张图片

非阻塞等待实验

因为是非阻塞式等待,所以父进程在等待期间可以做其他事情。

我们让父进程进程三个任务,下载任务,打印日志任务,展示信息任务,任务时长为5秒:Linux 进程等待_第19张图片

然后我们定义一个函数指针数组,然后把它tyepdef为一个函数指针类型:

也就是说   task_t就是我们的任务类型。

然后我们开始执行任务,

tasks[TASK_NUM]也就是有五个任务。

然后对五个任务进行初始化,最后添加任务。

调用:

  task_t tasks[TASK_NUM];     
  InitTasks(tasks,TASK_NUM);
  AddTask(tasks);               

定义:

对五个任务进行初始化,把每个任务初始化为NULL;

对任务数组添加任务,如果任务数组为NULL,那就把 t 这个任务添加到任务数组中;

用return 1表示添加任务成功,return 0表示任务数组内全为有效内容,不再添加,退出。

  
  void IninTasks(task_t tasks[],int num)
  {
    for(int i=0;i

接下来我们就开始添加执行任务了,把download,PrinfLog,show三个任务添加:

 AddTask(tasks,download);    
 AddTask(tasks,PrintLog);   
 AddTask(tasks,show);   

执行任务:

 void executeTask(task_t task[],int num)
  {
    //安全检查
    for(int i=0;i

然后把executeTask()添加到父进程等待那里,让父进程可以在等待期间移除,添加,执行任务:
 

  else if(rid==0)
      {
          //走到这里,父进程等子进程成功,但是子进程还未退出。                                 
          printf("father to do other things\n");
  
           executeTask(tasks,TASK_NUM);//父进程在等待期间也可以移除,添加等任务
      }

我们可以看见子进程在执行work(),父进程在等待期间做其他事情,下载,打印日志,展示信息:

Linux 进程等待_第20张图片

#include 
#include
#include
#include
#include



#define TASK_NUM  5


typedef void (*task_t)();
//
void download()
{
    printf("this is a dowm;load task is running\n");
}
void PrintLog()
{
    printf("this is a wirte log task\n");
}
void show()
{
    printf("this is ashow info task is running\n");
}



void InitTasks(task_t tasks[],int num)
{
  for(int i=0;i0)
    {
      //走到这里,说明父进程等待子进程成功
      printf("child quit sucess,exit code:%d,exit sigin:%d\n ",(status<<8)&0xFF,status&0x7F);
    }

    else if(rid==0)
    { 
        printf("#########################################################");
        //走到这里,父进程等子进程成功,但是子进程还未退出。
        printf("father to do other things\n");

        executeTask(tasks,TASK_NUM);//父进程在等待期间也可以移除,添加等任务
        sleep(1);
    }
    else 
    {
        //走到这里就只有最后一种情况:rid<0,说明有异常,一般都是pid写错了
        printf("wait fail\n");
        break;
    }
   }
return 0;
}

总结:非阻塞等待可以让父进程在等待期间做其他任务

你可能感兴趣的:(Linux,java,linux,算法)