进程控制【Linux】

文章目录

创建一批子进程

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define N 5

void runChild()
{
    int cnt = 10;
    while (cnt != 0)
    {
        printf("i am a child : %d , ppid:%d\n", getpid(), getppid());
        sleep(1);
        cnt--;
    }
}

int main()
{
    int i = 0;
    for (; i < N; i++)
    {
        pid_t id = fork();
        if (id == 0)
        {
            //子进程
            runChild();
            exit(0);
        }
        //父进程
        //父进程继续循环
    }
    sleep(1000);
    return 0;
}

进程终止

进程退出只有三种情况:

代码运行完毕,结果正确。

代码运行完毕,结果不正确

代码异常终止(进程崩溃)

代码异常,本质可能就是代码没有跑完

此时进程的退出码无意义,我们不关心退出码

代码是否正确,统一会采用进程的退出码来进行判断

查看该进程的进程退出码

$?:保存的是最近一次进程退出的时候的退出码

powershell 复制代码
[cxq@iZ7xviiy0goapxtblgih6oZ lesson15]$ echo $?

查看错误码

errno

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main() 
{
    int ret = 0;
    char *p = (char*)malloc(1000 * 1000 * 1000 * 4);

    if (p == NULL) 
    {
    //errno 返回的是错误码 ,strerror可以知道错误原因
        printf("malloc error, %d %s\n", errno, strerror(errno));
        ret = errno;
    } 
    else 
    {
        printf("malloc success\n");
    }
    return ret;
}

进程出现异常,本质是我们的进程收到了对应的信号

exit与return的区别

exit在任意地方被调用,都表示调用进程直接退出

return只表示当前函数返回,还会继续向后运行

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void show()
{
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    exit(13);//exit在任意地方被调用,都表示调用进程直接退出,如果此时换成return,return只表示当前函数返回,还会继续向后运行
    printf("end show\n");
    printf("end show\n");
    printf("end show\n");
    printf("end show\n");
    printf("end show\n");
}

int main()
{
    show();
    printf("hello Linux\n");
    return 12;
}
cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void show()
{
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    return ;//return只表示当前函数返回,还会继续向后运行
    exit(13);//exit在任意地方被调用,都表示调用进程直接退出
    printf("end show\n");
    printf("end show\n");
    printf("end show\n");
    printf("end show\n");
    printf("end show\n");
}

int main()
{
    show();
    printf("hello Linux\n");
    return 12;
}

_exit :

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void show()
{
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    printf("hello show\n");
    _exit(14);
    printf("end show\n");
    printf("end show\n");
    printf("end show\n");
    printf("end show\n");
    printf("end show\n");
}

int main()
{
    show();
    printf("hello Linux\n");
    return 12;
}

_exit 与exit的区别

使用exit函数退出进程也是我们常用的方法,exit函数可以在代码中的任何地方退出进程,并且exit函数在退出进程前会做一系列工作

1、执行用户通过atexit或on_exit定义的清理函数。

2、关闭所有打开的流,所有的缓存数据均被写入。

3、调用_exit函数终止进程。

_exit()是系统调用接口,exit()是库函数

当把exit改成_exit时,使用_exit终止进程,不会刷新缓冲区的数据,所以缓冲区当中的数据将不会被输出,就不会看到you can see me 了

但是exit在终止进程之前,会冲刷缓冲区,将缓冲区的数据输出

printf一定是先把数据写入缓冲区中,合适的时候,再进行刷这个缓冲区,缓冲区绝对不在内核中,在用户区

进程等待

进程等待:通过系统调用wait/waitpid,来进行对子进程进行状态检测与回收的功能

1、子进程退出,父进程如果不读取子进程的退出信息,子进程就会变成僵尸进程,进而造成内存泄漏

2、进程一旦变成僵尸进程,那么就算是kill -9命令也无法将其杀死,因为谁也无法杀死一个已经死去的进程

3、对于一个进程来说,最关心自己的就是其父进程,因为父进程需要知道自己派给子进程的任务完成的如何

4、父进程需要通过进程等待的方式,回收子进程资源,获取子进程的退出信息

父进程通过调用wait/waitpid进行僵尸进程的回收问题

监控脚本

powershell 复制代码
while :; do ps ajx |head -1 && ps ajx | grep testWait | grep -v grep  ; sleep 1 ; echo "------------"  ; done

wait是等待任意一个子进程退出

如果是多个子进程僵尸,如何利用wait回收?

shell 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h> 
  6 #define N 10 
  7   void     Runchild ()
  8 {
  9   int cnt =5 ;
 10   while(cnt)
 11   {
 12     printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
 13     sleep(1);
 14     cnt--;
 15   }
 16 }
 17 int main()
 18 {
 19   for( int i =0 ; i < N ;i ++ )
 20   {
 21     pid_t id  =  fork();
 22     if( id ==0  )              
 23     {                   
 24       //子进程
 25       Runchild ();
 26       exit(0);    
 27     }                                                                                                                                                      
 28     //父进程      
 29     printf("create child process: %d success\n", id);
 30   }                                                  
 31   sleep(10);                                         
 32   //等待                                             
 33   for( int i = 0 ; i < N  ; i++ )
 34   {                              
 35       pid_t id   =  wait(NULL);  
 36      if( id>0 )                  
 37      {                         
 38        printf("wait %d success\n", id);
 39      }  
 40 
 41   }
 42 
 43 sleep(5);
 44   return 0 ;
 45 }

如果父进程在等待子进程,如果一个子进程或多个子进程不退出 ,父进程会在调用wait时,父进程就一直拿不到子进程的pid , 父进程会一直等待子进程退出,父进程这种状态叫阻塞等待

powershell 复制代码
 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h> 
  6 #define N 10 
  7   void     Runchild ()
  8 {
  9   int cnt =5 ;
 10   while(1)
 11   {
 12     printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
 13     sleep(1);
 14     cnt--;
 15   }
 16 }
 17 int main()
 18 {
 19   for( int i =0 ; i < N ;i ++ )
 20   {
 21     pid_t id  =  fork();
 22     if( id ==0  )
 23     {
 24       //子进程
 25       Runchild ();
 26       exit(0);
 27     }
 28     //父进程 
 29     printf("create child process: %d success\n", id);
 30   }
 31 //  sleep(10);                                                                                                                                             
 32   //等待
 33   for( int i = 0 ; i < N  ; i++ )
 34   {
 35       pid_t id   =  wait(NULL);
 36      if( id>0 )
 37      {
 38        printf("wait %d success\n", id);
 39      }
 40 
 41   }
 42 
 43 sleep(5);
 44   return 0 ;
 45 }

waitpid

等待指定子进程或任意子进程

waitpid的返回值:

1、等待成功返回被等待进程的pid。

2、如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0。

3、如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在。

pid:待等待子进程的pid,若设置为-1,则等待任意子进程

status:输出型参数(通过指针,把函数内部的数据带出来)

获取子进程的退出状态,不关心可设置为NULL

status:操作系统会去找对应id的子进程,并且等待该子进程,如果该子进程退出 ,操作系统会将子进程的退出信息拷贝到status指针所指向的变量当中

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h> 
  6 #define N 10 
  7   void     Runchild ()
  8 {
  9   int cnt =5 ;
 10   while(1)
 11   {
 12     printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
 13     sleep(1);
 14     cnt--;
 15   }
 16 }
 17 int main()
 18 {
 45   pid_t id = fork() ;
 46   if(id <0)
 47   {
 48     perror("fork");
 49     return 1 ;
 50   }
 51   else if( id == 0 )
 52   {
 53    //child 
 54    
 55    int cnt =5 ;
 56    while(cnt)
 57    {
 58      printf("I am a child ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
 59      cnt -- ;
 60      sleep(1);
 61    }
 62      exit(1);
 63   }
 64    else // parent 
 65   {
 66     int cnt =10  ;
 67     while(cnt )
 68     {
 69      printf("I am a parent ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);                                                                       
 70      cnt -- ;
 71      sleep(1); 
 72     }
 73     // pid_t ret = wait(NULL);
 74      
 75    int status =0 ;//输出型参数
 76    //操作系统会去找对应id的子进程,并且等待该子进程,如果该子进程退出 ,操作系统会将子进程的退出信息拷贝到status指针所指向的变量当中
 77    pid_t  ret = waitpid( id, &status , 0 );
 78 
 79      //等待成功
 80      if( ret ==id ) //id是子进程的pid
 81      {
 82        //status存放的是子进程的退出信息
 83        printf("wait success,ret:%d, status: %d\n", ret, status);
 84      }
 85 sleep(5);
 86 
 87 
 88   }
 89   return 0 ;
 90 }

为什么status是256 ?

在子进程中 ,exit函数里面,我们设置的是1

退出码设置的是1 ,也就是00000001,00000001就是256

子进程退出,一共会有3种退出场景

1、代码运行完毕,结果正确

2、代码运行完毕,结果不正确

3、代码异常终止

父进程等待,需要获得子进程退出的哪些信息?

1、子进程代码是否异常

2、如果没有异常,通过退出码判断来结果是否正确

不同的退出码表示不同的出错原因

status是一个整型变量,但status不能简单的当作整型来看待,status的不同比特位所代表的信息不同,具体细节如下(只研究status低16比特位)

cpp 复制代码
 1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h> 
  6 #define N 10 
  7   void     Runchild ()
  8 {
  9   int cnt =5 ;
 10   while(1)
 11   {
 12     printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
 13     sleep(1);
 14     cnt--;
 15   }
 16 }
 17 int main()
 18 {
 45   pid_t id = fork() ;
 46   if(id <0)
 47   {
 48     perror("fork");
 49     return 1 ;
 50   }
 51   else if( id == 0 )                                                                                                                                                                       
 52   {
 53    //child 
 54    
 55    int cnt =5 ;
 56    while(cnt)
 57    {
 58      printf("I am a child ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
 59      cnt -- ;
 60      sleep(1);
 61    }
 62      exit(1);
 63   }
 64    else // parent 
 65   {
 66     int cnt =10  ;
 67     while(cnt )
 68     {
 69      printf("I am a parent ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
 70      cnt -- ;
 71      sleep(1); 
 72     }
 73     // pid_t ret = wait(NULL);
 74      
 75    int status =0 ;//输出型参数
 76    //操作系统会去找对应id的子进程,并且等待该子进程,如果该子进程退出 ,操作系统会将子进程的退出信息拷贝到status指针所指向的变量当中
 77    pid_t  ret = waitpid( id, &status , 0 );
 78 
 79      //等待成功
 80      if( ret ==id ) //id是子进程的pid
 81      {
 82        //status存放的是子进程的退出信息
 83        //7F 0111 1111 
 84        //0xFF
 85        printf("wait success,ret:%d, exit sig : %d, exit code:%d\n", ret, status&0x7F , (status>>8) &0xFF  ); //打印的分别是 id, 信号, 退出码
 86      }
 87 sleep(5);
 88 
 89 
 90   }
 91   return 0 ;
 92 }

通过收到信号判断是否异常

通过看退出码判断是否结果正确

系统当中提供了两个宏来获取退出码和退出信号

WIFEXITED(status):用于查看进程是否是正常退出,本质是检查是否收到信号

WEXITSTATUS(status):用于获取进程的退出码

cpp 复制代码
exitNormal = WIFEXITED(status);  //获取退出信号,判断是否正常退出
exitCode = WEXITSTATUS(status);  //获取退出码

例如:

cpp 复制代码
 1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h> 
  6 #define N 10 
  7   void     Runchild ()
  8 {
  9   int cnt =5 ;
 10   while(1)
 11   {
 12     printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
 13     sleep(1);
 14     cnt--;
 15   }
 16 }
 17 int main()
 18 {
 45   pid_t id = fork() ;
 46   if(id <0)
 47   {
 48     perror("fork");
 49     return 1 ;
 50   }
 51   else if( id == 0 )                                                                                                                                                                       
 52   {
 53    //child 
 54    
 55    int cnt =5 ;
 56    while(cnt)
 57    {
 58      printf("I am a child ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
 59      cnt -- ;
 60      sleep(1);
 61    }
 62      exit(1);
 63   }
 64    else // parent 
 65   {
 66     int cnt =10  ;
 67     while(cnt )
 68     {
 69      printf("I am a parent ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
 70      cnt -- ;
 71      sleep(1); 
 72     }
 73     // pid_t ret = wait(NULL);
 74      
 75    int status =0 ;//输出型参数
 76    //操作系统会去找对应id的子进程,并且等待该子进程,如果该子进程退出 ,操作系统会将子进程的退出信息拷贝到status指针所指向的变量当中
 77    pid_t  ret = waitpid( id, &status , 0 );
 78 
 79      //等待成功
 80      if( ret ==id ) //id是子进程的pid
 81      {
 82        //status存放的是子进程的退出信息
 83        //7F 0111 1111 
 84        //0xFF
 85        //printf("wait success,ret:%d, exit sig : %d, exit code:%d\n",      ret, status&0x7F , (status>>8) &0xFF  ); //打印的分别是 id, 信号, 退出码
               if(WIFEXITED(status) )
 88        {
 89            printf("进程是正常跑完的,退出码:%d\n",WIFEXITED(status));
 90        }
 91        else
 92        {
 93          printf("进程出异常了\n");
 94        }
 86      }
 87 sleep(5);
 88 
 89 
 90   }
 91   return 0 ;
 92 }

以下代码中同时创建了10个子进程

父进程再使用waitpid函数指定等待这10个子进程

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h>
  6 #define N 10 
  7   void     Runchild ()
  8 {
  9   int cnt =5 ;
 10   while(cnt)
 11   {
 12     printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
 13     sleep(1);
 14     cnt--;
 15   }
 16 }
 17 int main()
 18 {
     //创建多进程
 19   for( int i =0 ; i < N ;i ++ )
 20   {
 21     pid_t id  =  fork();
 22     if( id ==0  )
 23     {
 24       //子进程
 25       Runchild ();                                                                                                                                                                         
 26       exit(i);
 27     }
 28     //父进程 
 29     printf("create child process: %d success\n", id);
 30   }
 31 //  sleep(10);
 32   //多进程等待
 33   for( int i = 0 ; i < N  ; i++ )
 34   {
 35 //      pid_t id   =  wait(NULL);
 36      int status =0 ;
 37     pid_t id=    waitpid(-1 , &status ,0 );
 38 
 39      if( id>0 )
 40      {
 41        printf("wait %d success,exit code:%d\n", id,WEXITSTATUS(status ) )  ; //id ,退出码
 42      }
 43 
 44   }
 45 
 46 sleep(5);
108   return 0 ;
109 }

options:
1、options为0时,父进程阻塞等待
2、options为WNOHANG时,若等待的子进程没有结束,则waitpid函数直接返回0,不予以等待。若正常结束,则返回该子进程的pid

非阻塞轮询

options为WNOHANG时,父进程等待子进程中实现非阻塞

例如,父进程可以隔一段时间调用一次waitpid函数,若是等待的子进程尚未退出,则父进程可以先去做一些其他事,过一段时间再调用waitpid函数读取子进程的退出信息。

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h> 
  6 #define N 10 
  7   void     Runchild ()
  8 {
  9   int cnt =5 ;
 10   while(cnt)
 11   {
 12     printf("child pid:%d ,ppid%d\n",getpid() ,getppid());
 13     sleep(1);
 14     cnt--;
 15   }
 16 }
 17 int main()
 18 {
 48  pid_t id = fork() ;
 49  if(id <0)
 50  {
 51    perror("fork");                                                                                                                                                                         
 52    return 1 ;
 53  }
 54  else if( id == 0 )
 55  {
 56   //child 
 57   
 58   int cnt =3 ;
 59   while(cnt)
 60   {
 61     printf("I am a child ,pid:%d , ppid:%d cnt: %d\n", getpid() , getppid() ,cnt);
 62     cnt -- ;
 63     sleep(1);
 64   }
 65     exit(11);
 66  }
 67   else // parent 
 68  {
 78   int status =0 ;//输出型参数
 79   //操作系统会去找对应id的子进程,并且等待该子进程,如果该子进程退出 ,操作系统会将子进程的退出信息拷贝到status指针所指向的变量当中
 80   while(1)  //轮询
 81   {
 82 
 83   pid_t  ret = waitpid( id, &status , WNOHANG ); //非阻塞等待
 84 
 85     //等待成功
 86     if( ret >0 ) //id是子进程的pid
 87     {
 88       //status存放的是子进程的退出信息
 89       //7F 0111 1111 
 90       //0xFF
 91      // printf("wait success,ret:%d, exit sig : %d, exit code:%d\n", ret, status&0x7F , (status>>8) &0xFF  ); //打印的分别是 id, 信号, 退出码
 92      
 93       if(WEXITSTATUS(status) )
 94       {
 95           printf("进程是正常跑完的,退出码:%d\n",WEXITSTATUS(status));
 96       }
 97       else 
 98       {
 99         printf("进程出异常了\n");
100       }
101        break;
102     }
103     else if ( ret< 0 ) 
104     {
105      printf("wait failed\n");
106      break;
107     }
108     else  //ret==0
109     {
110     printf("子进程还没有退出我再等等\n") ;
111     sleep(1);
112     }
113   }
114   sleep(3);
115 
116 
117  }
118   return 0 ;
119 }


如果你觉得这篇文章对你有帮助,不妨动动手指给点赞收藏加转发,给鄃鳕一个大大的关注你们的每一次支持都将转化为我前进的动力!!!

相关推荐
xuanzdhc2 小时前
Linux 基础IO
linux·运维·服务器
愚润求学2 小时前
【Linux】网络基础
linux·运维·网络
bantinghy3 小时前
Linux进程单例模式运行
linux·服务器·单例模式
小和尚同志4 小时前
29.4k!使用 1Panel 来管理你的服务器吧
linux·运维
帽儿山的枪手4 小时前
为什么Linux需要3种NAT地址转换?一探究竟
linux·网络协议·安全
shadon1789 天前
回答 如何通过inode client的SSLVPN登录之后,访问需要通过域名才能打开的服务
linux
AWS官方合作商9 天前
AWS ACM 重磅上线:公有 SSL/TLS 证书现可导出,突破 AWS 边界! (突出新功能的重要性和突破性)
服务器·https·ssl·aws
小米里的大麦9 天前
014 Linux 2.6内核进程调度队列(了解)
linux·运维·驱动开发
程序员的世界你不懂9 天前
Appium+python自动化(三十)yaml配置数据隔离
运维·appium·自动化
算法练习生9 天前
Linux文件元信息完全指南:权限、链接与时间属性
linux·运维·服务器