• [ Linux ] 进程控制(下)----进程等待与进程程序替换



    前言

    本小节继续承接上文进程控制继续对进程控制中的进程等待和进程程序替换内容进行讲解!


    正文开始

    一、进程等待

    1.1 进程等待必要性

    • 之前在进程状态这篇博客中我们讲到:子进程退出,父进程如果不管不顾,就可能造成"僵尸进程"的问题,进而造成内存的泄漏!
    • 另外,进程一旦进程变成僵尸状态,那就刀枪不入,"杀人不眨眼"得kill -9也无能为力,因为谁也没有办法杀死一个已经死去的进程。
    • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如:子进程运行完成,结果对还是不对,或者是否正常退出。( 获取子进程的退出状态!,因为进程退出是有退出码的。)
    • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。

    1.2 进程等待的方法

    wait方法

    系统给我们提供了wait接口方法,wait()的方案可以解决挥手子进程Z状态,让子进程进入X状态。
    在这里插入图片描述

    #include
      2 #include<unistd.h>
      3 #include<string.h>
      4 #include<stdlib.h>
      5 #include<sys/wait.h>
      6 #include<sys/types.h>
      7 
      8 
      9 int main()
     10 {
     11     pid_t id=fork();
     12     if(id==0)
     13     {
     14         //child
     15         while(1)
     16         {
     17             printf("我是子进程,我正在运行...pid:%d\n",getpid());
     18             sleep(1);
     19         }
     20     }
     21     else                                                                                                                                     
     22     {
     23         printf("我是父进程,pid:%d,我准备等待子进程啦!!!\n",getpid());
     24         sleep(30);
     25         pid_t ret=wait(NULL);
     26         if(ret<0)
     27         {
     28             printf("等待失败!\n");
     29         }
     30         else 
     31         {
     32             printf("等待成功!result:%d\n",ret);
     33         }
     34         sleep(20);
     35     }
     36     return 0;
     37 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    运行我们的程序

    在这里插入图片描述

    在我们另一侧执行

    kill -0 子进程pid

    子进程就会进入僵尸状态(Z状态)

    在这里插入图片描述
    然后打开我们的监控脚本查看子进程的退出状态变化!

    while :; do ps ajx |head -1 && ps ajx |grep myproc |grep -v grep; echo “####################”; sleep 1;done

    "#"是作为一个区分符,方便我们查看进程状态
    在这里插入图片描述


    父进程等待成功后,返回子进程的pid。
    在这里插入图片描述
    最后程序结束,父进程也就退出了。

    wait_pid()

    我们首先与wait进行比较,wait是等待任意一个退出的子进程!

    今天我们的重点是关于wait_pid()的讲解!
    在这里插入图片描述
    pid_t:(返回值)

    • >0:等待子进程成功,返回值就是子进程的pid
    • <0:等待失败

    waitpid的第一个参数pid

    • >0:是几,就代表等待哪一个子进程。
    • -1:等待任意进程

    waitpid的第二个参数status

    • 这个参数,是一个输出型参数(通过调用这个函数,我要得到什么信息或者数据)
    • 进程退出,如果异常退出,是因为这个进程收到了特定的信号!!–>获取退出信号
    • 等等之后了解。
      在这里插入图片描述
      带大家看看内核数据结构里面的描述!
      在这里插入图片描述
      对于int* status
      今天我们只需要关心整数低16个比特位
      在这里插入图片描述

    8-15个比特位代表程序的退出状态
    0-7比特位代表程序推出的信号

    waitpid的第三个参数options

    • 现在我们先来第一个选项为0是进行阻塞等待。

    下来我们进行验证!

      1 #include<stdio.h>
      2 #include<unistd.h>
      3 #include<string.h>
      4 #include<stdlib.h>
      5 #include<sys/wait.h>
      6 #include<sys/types.h>
      7 
      8 
      9 int main()
     10 {
     11     pid_t id=fork();
     12     if(id==0)
     13     {
     14         //child
     15         int cnt=5;
     16         while(1)
     17         {
     18             printf("我是子进程,我正在运行...pid:%d\n",getpid());
     19             sleep(1);
     20             cnt--;
     21             if(cnt==0)                                                                                                                       
     22             {
     23                 break;
     24             }
     25         }
     26         exit(13);
     }
     28     else 
     29     {
     30         printf("我是父进程,pid:%d,我准备等待子进程啦!!!\n",getpid());
     31         int status=0;
     32         pid_t ret=waitpid(id,&status,0);
     33         if(ret>0)
     34         {
     35             printf("wait success, ret:%d,我所等待的子进程的退出码:%d\n",ret,(status>>8)&0xFF);
     36         }
     37		}
     38}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    子进程的退出码为13,那么我们能如愿得到子进程的退出码吗?
    下来我们看结果!!!
    在这里插入图片描述
    我们如愿得到了结果:子进程的退出码!

    下来我们验证关于status的低七位的含义

      1 #include<stdio.h>
      2 #include<unistd.h>
      3 #include<string.h>
      4 #include<stdlib.h>
      5 #include<sys/wait.h>
      6 #include<sys/types.h>
      7 
      8 
      9 int main()
     10 {
     11     pid_t id=fork();
     12     if(id==0)
     13     {
     14         //child
     16         while(1)
     17         {
     18             printf("我是子进程,我正在运行...pid:%d\n",getpid());
     19             sleep(1);
     25         }
     26         exit(0);
     27     }
     28     else 
     29     {
     30         printf("我是父进程,pid:%d,我准备等待子进程啦!!!\n",getpid());                                                                     
     31         int status=0;
     32         pid_t ret=waitpid(id,&status,0);
     33         if(ret>0)
     34         {
     35             printf("wait success, ret:%d,我所等待的子进程的退出码:%d,退出信号是:%d\n",ret,(status>>8)&0xFF,(status)&0x7F);
     36         }
     37         
     38     }    
     39  }       
     40         
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在这里插入图片描述
    我们使用kill -9 杀掉子进程后,果然得到了子进程的异常信号。
    在这里插入图片描述
    不同的异常退出会得到不同的信号!

    下来看看Linux下的退出信号有哪些

    kill -l

    在这里插入图片描述
    我们可以看到没有0号退出信号!!!

    接下来再带大家看看除0错误,这个系统会自动退出,不需要我们杀掉他!

      1 #include<stdio.h>
      2 #include<unistd.h>
      3 #include<string.h>
      4 #include<stdlib.h>
      5 #include<sys/wait.h>
      6 #include<sys/types.h>
      7 
      8 
      9 int main()
     10 {
     11     pid_t id=fork();
     12     if(id==0)
     13     {
     14         //child
     16         while(1)
     17         {
     18             printf("我是子进程,我正在运行...pid:%d\n",getpid());
     19             sleep(10);
      				int a=10/0;
     25         }
     26         exit(0);
     27     }
     28     else 
     29     {
     30         printf("我是父进程,pid:%d,我准备等待子进程啦!!!\n",getpid());                                                                     
     31         int status=0;
     32         pid_t ret=waitpid(id,&status,0);
     33         if(ret>0)
     34         {
     35             printf("wait success, ret:%d,我所等待的子进程的退出码:%d,退出信号是:%d\n",ret,(status>>8)&0xFF,(status)&0x7F);
     36         }
     37         
     38     }    
     39  }       
     40  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述
    还有空引用等等错误,也会导致程序异常终止,返回信号码给父进程。
    注意:wait()/waitpid()是系统调用接口!

    退出码和信号码

    关于退出码和信号码,那么先看那个呢?—>信号码!

    一旦进程出现异常退出后,只关心退出信号,退出码没有任何意义!

    status:
    WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
    WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

    #include
      2 #include<unistd.h>
      3 #include<string.h>
      4 #include<stdlib.h>
      5 #include<sys/wait.h>
      6 #include<sys/types.h>
      7 
      8 
      9 int main()
     10 {
     11     pid_t id=fork();
     12     if(id==0)
     13     {
     14         //child
     15         //int cnt=5;
     16         while(1)
     17         {
     18             printf("我是子进程,我正在运行...pid:%d\n",getpid());
     19             sleep(10);
     20             break;
     27         }                                                                                                                                    
     28         exit(10);
     29     }
     30     else 
     31     {
     32         printf("我是父进程,pid:%d,我准备等待子进程啦!!!\n",getpid());
     33         int status=0;
     34         pid_t ret=waitpid(id,&status,0);
     35         if(ret>0)
     36         {
     37             if(WIFEXITED(status))
     38             {
     39                 printf("子进程是正常退出的,退出码:%d\n",WEXITSTATUS(status));
     40             }   
     43         }
     44}         
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    在这里插入图片描述

    阻塞等待和非阻塞等待

    当我们调用某些函数的时候,因为条件不就绪,需要我们阻塞等待。
    本质:就是当前进程自己变成阻塞状态,等待条件就绪的时候,在被唤醒!

    非阻塞的理解

    对于waitpid()的最后一个参数options
    0:标识阻塞等待。

    在子进程休眠的时候,父进程在此期间可以去做别的事情。
    多次调用非阻塞接口—轮询检测!

    options:
    WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

    下来带大家看看waitpid的返回值
    在这里插入图片描述
    修改父进程中的代码即可!

    
    //基于非阻塞的轮询等待方案
     33         int status=0;
     34         while(1)
     35         {
     36             pid_t ret=waitpid(-1,&status,WNOHANG);                                                                                           
     37             if(ret>0)
     38             {
     39                 printf("子进程是正常退出的,退出码:%d\n",WEXITSTATUS(status));
     40                 break;
     41             }
     42             else if(ret==0)
     43             {
     44                 //等待成功了,但是子进程没有退出
     45                 printf("子进程还没好,那我父进程在做其他事情...\n");
     46                 sleep(1);
     47             }
     48             else
     49             {
     50                 //出错了,暂时不处理
     51 
     52             }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    二、进程程序替换

    2.1 进程程序替换是什么

    2.1.1 概念

    通过之前的学习,我们知道子进程执行的是父进程的代码片段。

    如果我们想让创建出来的子进程,执行的是全新的程序呢?

    这里我们就需要用到进程程序替换。

    2.1.2 原理

    1.将磁盘中的程序,加载入内存结构

    2.重新建立页表映射,谁执行程序替换,就重新建立谁的映射(子进程)
    效果:让我们的父进程和子进程彻底分离,并让子进程执行一个新的程序。

    在这里插入图片描述
    那么问题来了!!!

    这个过程有没有创建新的进程呢?

    答:我们肯定没有创建新的进程,只不过让子进程执行了新的程序。

    2.2 为什么要进行程序替换

    我们一般在服务器设计(linux编程)的时候,往往需要子进程干两件种类的事情

    1. 让子进程执行父进程的代码片段(服务器代码)
    2. 让子进程执行磁盘中的一个全新的程序(shell,想让客户端执行对应的程序,通过我们的进程,执行其他人写的进程的代码等等)

    例如我们用C语言的进程去掉java/python/C++等等的程序。

    2.3 如何进行程序替换

    由OS完成,所以我们要调用系统接口。

    首先带大家来看看系统接口有什么!
    在这里插入图片描述

    我们如果要执行一个全新的程序,我们需要做几件事情呢?

    1. 程序本质就是一个磁盘上的文件,所以我们需要先找到这个程序在哪里
    2. 程序可能携带选项进行执行(也可以不携带),然后告诉OS,我要怎么执行这个程序?(要不要带选项)

    使用接口

    execl

    在这里插入图片描述
    其中参数列表中:“…” 叫做可变参数,说白了就是可以按照用户的意愿传入参数的大小个数,如果还不理解,大家肯定都用过C语言中的printf函数吧,printf有没有规定你只能打印几个参数呢?没有的,这是根据用户自己来定义的!这就是可变参数

      1 #include<stdio.h>
      2 #include<unistd.h>
      3 #include<sys/wait.h>
      4 #include<stdlib.h>
      5 
      6 int main()
      7 {
      8     printf("我是一个进程,我的pid是:%d\n",getpid());
      9     execl("/usr/bin/ls","ls","-l","-a",NULL);
     10     printf("我执行完毕了,我的pid是:%d\n",getpid());                                            
     15     return 0;
     16 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    执行top命令

      1 #include<stdio.h>
      2 #include<unistd.h>
      3 #include<sys/wait.h>
      4 #include<stdlib.h>
      5 
      6 int main()
      7 {
      8     printf("我是一个进程,我的pid是:%d\n",getpid());
      9     //execl("/usr/bin/ls","ls","-l","-a",NULL);
     10     execl("/usr/bin/top","top",NULL);                                                            
     11     printf("我执行完毕了,我的pid是:%d\n",getpid());
     16     return 0;
     17 }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述
    执行完以上的代码,我们发现一个问题!!!

    最后一句代码为什么没有被打印出来呢!!!

    在这里插入图片描述
    因为进程一旦替换成功,是将当前进程的代码和数据全部替换了!!!

    后面的printf是代码吗??有没有被替换呢??当然,已经早就被替换了!!改代码不存在了!!

    所以这个程序替换函数,用不用判断返回值?为什么?

    答:不用判断返回值,因为只要成功了,就不会有返回值。而失败的时候,必然会继续向后执行!!最多通过返回值得到什么原因导致的替换失败!

    引入进程创建

      6 int main()          
      7 {                 
      8     printf("我是一个进程,我的pid是:%d\n",getpid());
      9     pid_t id=fork();
     10     if(id==0)
     11     {                                                
     12         //child                                                                                                                              
     13         //我们想让子进程执行新的程序,以前是执行父进程的代码片段
     14         printf("我是子进程,我的pid是:%d\n",getpid());
     15         execl("/usr/bin/ls","ls","-l","-a",NULL);
     16                                                                 
     17         exit(1);                                       
     18     }                                            
     19     //一定是父进程
     20     int status=0;
     21     int ret=waitpid(id,&status,0);
     22     if(ret==id)   
     23     {            
     24         sleep(2);                 
     25         printf("父进程等待成功!\n");
     26     }
     27                  
     28     return 0;                        
     29 }    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述
    子进程进行程序替换的时候会不会影响父进程呢?

    答:不会,因为进程具有独立性。

    为什么?如何做到的?

    因为数据层面发生了写时拷贝!当程序替换的时候,我们可以理解成为,代码和数据都发生了写时拷贝,完成了父子的分离。

    execv

    在这里插入图片描述
    他的第二个参数是一个指针数组,他和execl的区别就是把选项作为参数放入指针数组,然后进行传参!

    int main()
      7 {
      8     printf("我是一个进程,我的pid是:%d\n",getpid());
      9     pid_t id=fork(); 
     10     if(id==0)
     11     {
     12         //child
     13         //我们想让子进程执行新的程序,以前是执行父进程的代码片段
     14         printf("我是子进程,我的pid是:%d\n",getpid());
     15       char *const argv_[]={
     16         (char*)"ls",
     17         (char*)"-a",
     18         (char*)"-l",
     19          NULL
     20          };
     21       execv("/usr/bin/ls",argv_);                                                                                                            
     22         exit(1);
     23     }
     24     //一定是父进程
     25     int status=0;
     26     int ret=waitpid(id,&status,0);
     27     if(ret==id)
     28     {
     29         sleep(2);
     30         printf("父进程等待成功!\n");
     31     }
     32 
     33     return 0;
     34 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述
    区分execl和execv
    在这里插入图片描述

    execlp

    在这里插入图片描述
    相比于execl这个接口的第一个参数变化。

    我们执行指令的时候,默认的搜索路径,在哪里搜索呢?
    环境变量PATH!

    p代表PATH

    所以命名中带p的,可以不带路径,只要说出你要执行哪一个程序即可!

    #include
    #include
    #include
    #include
    
    int main()
    {
        printf("我是一个进程,我的pid是:%d\n",getpid());
        pid_t id=fork(); 
        if(id==0)
        {
            //child
            //我们想让子进程执行新的程序,以前是执行父进程的代码片段
            printf("我是子进程,我的pid是:%d\n",getpid());
          char *const argv_[]={
            (char*)"ls",
            (char*)"-a",
            (char*)"-l",
             NULL
             };
          	execlp("ls","ls","-a","-l",NULL);//这里出现了两个ls,含义一样吗?不一样!
            exit(1);
        }
        //一定是父进程
        int status=0;
        int ret=waitpid(id,&status,0);
        if(ret==id)
        {
            sleep(2);
            printf("父进程等待成功!\n");
        }
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述
    照样也是可以执行的!

    execvp

    在这里插入图片描述
    和前面的知识我们结合起来就了解了这个接口的参数!

    #include
    #include
    #include
    #include
    
    int main()
    {
        printf("我是一个进程,我的pid是:%d\n",getpid());
        pid_t id=fork(); 
        if(id==0)
        {
            //child
            //我们想让子进程执行新的程序,以前是执行父进程的代码片段
            printf("我是子进程,我的pid是:%d\n",getpid());
          char *const argv_[]={
            (char*)"ls",
            (char*)"-a",
            (char*)"-l",
             NULL
             };
            execvp("ls",argv_);
            exit(1);
        }
        //一定是父进程
        int status=0;
        int ret=waitpid(id,&status,0);
        if(ret==id)
        {
            sleep(2);
            printf("父进程等待成功!\n");
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在这里插入图片描述

    execle

    在这里插入图片描述
    这里的前两个接口都非常熟悉了,这里最后一个接口叫做环境变量。那么为什么要有这个接口呢?

    说到环境变量之前我们先来看一下这个问题,我们刚刚提到过,进程替换可以让我们执行其他语言写的程序,那么我们怎么来执行呢?(我们使用execl 函数来调用)

    我们现在的目标是想用我们写的myexec.c把mycmd.cpp调用起来,那么怎么来用呢?
    myexec.c

    #include
    #include
    #include
    #include
    
    int main()
    {
        printf("我是一个进程,我的pid是:%d\n",getpid());
        pid_t id=fork(); 
        if(id==0)
        {
            //child
            printf("我是子进程,我的pid是:%d\n",getpid());
            execl("/home/hulu/lesson10/mycmd","./mycmd",NULL);
            exit(1);
        }
        //一定是父进程
        int status=0;
        int ret=waitpid(id,&status,0);
        if(ret==id)
        {
            sleep(2);
            printf("父进程等待成功!\n");
        }
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    mycmd.cpp

    #include
    #include
    int main()
    {
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述
    我们当前使用的是绝对路径来吊用我的mycmd程序!
    当然我们也可以使用相对路径来调用。
    相对路径调用

    #include
    #include
    #include
    #include
    
    int main()
    {
        printf("我是一个进程,我的pid是:%d\n",getpid());
        pid_t id=fork(); 
        if(id==0)
        {
            //child
            printf("我是子进程,我的pid是:%d\n",getpid());
            execl("./mycmd","mycmd",NULL);
            exit(1);
        }
        //一定是父进程
        int status=0;
        int ret=waitpid(id,&status,0);
        if(ret==id)
        {
            sleep(2);
            printf("父进程等待成功!\n");
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述
    我们再运行发现,仍然是可以成功的。当然也可以用我们的程序来调用python语言,shell脚本语言等等。

    程序替换调用python的程序

    #include
    #include
    #include
    #include
    
    int main()
    {
        printf("我是一个进程,我的pid是:%d\n",getpid());
        pid_t id=fork(); 
        if(id==0)
        {
            //child
            printf("我是子进程,我的pid是:%d\n",getpid());
            execl("/usr/bin/python3","python3","test.py",NULL);
            exit(1);
        }
        //一定是父进程
        int status=0;
        int ret=waitpid(id,&status,0);
        if(ret==id)
        {
            sleep(2);
            printf("父进程等待成功!\n");
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
      1 #!/usr/bin/python3                                                              
      2                                                                                 
      3 print("hello python!");                                                         
      4 print("hello python!");                                                         
      5 print("hello python!");                                                         
      6 print("hello python!");                                                         
      7 print("hello python!");                                                         
      8 print("hello python!");                                                         
      9 print("hello python!"); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    程序替换调用shell的程序

    #include
    #include
    #include
    #include
    
    int main()
    {
        printf("我是一个进程,我的pid是:%d\n",getpid());
        pid_t id=fork(); 
        if(id==0)
        {
            //child
            printf("我是子进程,我的pid是:%d\n",getpid());
            execl("/usr/bin/bash","bash","test.sh",NULL);
            exit(1);
        }
        //一定是父进程
        int status=0;
        int ret=waitpid(id,&status,0);
        if(ret==id)
        {
            sleep(2);
            printf("父进程等待成功!\n");
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    #!/usr/bin/bash 
    
    cnt=0;
    
    while [ $cnt -le 10  ]
    do 
        echo "hello shell!"
        let cnt++
    done
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    在这里插入图片描述
    所以我们使用ece系列的系统级函数可以把任何语言耦合在一起!

    谈完这个话题我们再来谈谈环境变量,execle这个函数多了一个e,这个e就是环境变量,如果你想给这个函数传入环境变量,我们就可以传入环境变量。
    在这里插入图片描述

    首先我们先来传入一个系统存在的环境变量,然后让我们使用myexec.c程序去调用这个函数

    #include
    #include
    int main()
    {
        
        std::cout<<"PATH:"<<getenv("PATH")<<std::endl;
        std::cout<<"-----------------"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    发现没有任何问题,那么如果我们想 传入一个自己手动写的环境变量呢?

    #include
    #include
    int main()
    {
        
        std::cout<<"PATH:"<<getenv("PATH")<<std::endl;
        std::cout<<"-----------------"<<std::endl;
        std::cout<<"MYPATH"<<getenv("MYPATH")<<std::endl;
        std::cout<<"--------------"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
    
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述
    运行到这里我们可以发现mycmd.cpp中下面的代码没有打出来,很显然是程序崩溃了!!!

    这是因为环境变量"MYPATH"在系统中是不存在的,用函数获取,获取失败了,直接就退出进程!
    在这里插入图片描述
    那么我想让我调用这个进程手动传入环境变量呢?这里我们可以调用系统接口execle,相比于execl多了一个e,这个e就代表的是环境变量!

    #include
    #include
    #include
    #include
    
    int main()
    {
        printf("我是一个进程,我的pid是:%d\n",getpid());
        pid_t id=fork(); 
        if(id==0)
        {
            //child
            printf("我是子进程,我的pid是:%d\n",getpid());
            char*const env_[]={
                (char*)"MYPATH=YOU Can See Me!\n",
                NULL
            };
    
            execle("./mycmd","mycmd",NULL,env_);
        }
        //一定是父进程
        int status=0;
        int ret=waitpid(id,&status,0);
        if(ret==id)
        {
            sleep(2);
            printf("父进程等待成功!\n");
        }
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述
    但是运行后,我们还是可以发现这个进程崩溃了,还是没有打印出代码!

    接下来我们屏蔽一些代码

    #include
    #include
    int main()
    {
        
        //std::cout<<"PATH:"<
        std::cout<<"-----------------"<<std::endl;
        std::cout<<"MYPATH"<<getenv("MYPATH")<<std::endl;
        std::cout<<"--------------"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
        std::cout<<"hello C++"<<std::endl;
    
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述
    这里我们发现我们导入的环境变量出现了!

    我们很好奇为什么呢?依照上面两个实验我们可以得出什么结论呢?

    我们可以得出execle添加环境变量给目标程序的时候是覆盖式传入的!!!

    我们自己传入系统的环境变量给我们程序替换的程序

    #include
    #include
    #include
    #include
    
    int main()
    {
        //环境变量的指针声明
        extern char** environ;
        
        printf("我是一个进程,我的pid是:%d\n",getpid());
        pid_t id=fork(); 
        if(id==0)
        {
            //child
            printf("我是子进程,我的pid是:%d\n",getpid());
            char* const env_[]={
                (char*)"MYPATH=YOUCanSeeMe!!",
                NULL
            };
    
            execle("./mycmd","mycmd",NULL,environ);
        }
        //一定是父进程
        int status=0;
        int ret=waitpid(id,&status,0);
        if(ret==id)
        {
            sleep(2);
            printf("父进程等待成功!\n");
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在这里插入图片描述
    这里我们看出还是程序崩溃了!
    因为我们MYPATH环境变量还没有导入!

    export MYPATH=“疯狂星期四!!”

    在这里插入图片描述

    讲完这些接口我们发现还剩下的其他接口参数大致相同,我们掌握上面几个接口之后,我们就可以使用其他的接口!至此,进程控制结束,下篇博客我来带大家简易实现一个shell!!!


    (本章完!)

  • 相关阅读:
    el-cascader 最后一级不显示出来
    PIE-Engine:房山区洪涝灾害风险评价
    JAVA高级技术入门(单元测试,反射,注解,动态代理)
    嵌入式实操----基于RT1170 AWTK1.6版本库编译(三十)
    Git 之 push 代码后,如何回退/回滚到之前的版本的方法
    Spring封装数据结果
    (6)SpringCloud-Spring Boot项目详细搭建步骤
    视觉语言跨模态特征语义相似度计算改进--表征空间维度语义依赖感知聚合算法 ACM MM
    Webpack5 Asset Module 使用小结
    Mycat2【java提高】
  • 原文地址:https://blog.csdn.net/m0_61560468/article/details/127584557