【lesson16】进程控制之进程替换(2)

发布时间:2023年12月18日

进程替换怎么用?

创建子进程时使用

我们为什么要创建子进程并且用子进程去进行进程替换?
写个代码来增加理解:

#include <stdio.h>    
#include <unistd.h>    
#include <sys/types.h>    
#include <sys/wait.h>    
    
int main()    
{    
  pid_t id = fork();    
  if(id == 0)    
  {    
    printf("子进程开始运行,pid:%d\n",getpid());                                                                                                                         
    sleep(1);    
    execl("/usr/bin/ls","ls","-a","-l",NULL);    
  }    
  else    
  {    
    printf("父进程开始运行,pid:%d\n",getpid());    
    int status = 0;    
    pid_t res = waitpid(-1,&status,0);    
    if(res > 0)    
    {    
      printf("等待子进程成功\n");    
    }    
    
  }    
  return 0;    
}

从代码我们可以很容易看出:
我们用子进程做任务,这样就可以让父进程阻塞式等待子进程做任务的结果,父进程只要派发任务即可。

Xshell的模拟实现思路
在这里插入图片描述

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

int main()
{
  while(1)
  {   //1.首先肯定是要一直循环的,因为我们的xshell一直在运行
      //2.显示提示行
      //3.获取用户输入的字符串
      //4.对字符串进行解析
      //5.执行以下代码
      pid_t id = fork();
      if(id == 0)
      {
        printf("子进程开始运行,pid:%d\n",getpid());
        sleep(1);
        execl("/usr/bin/ls","ls","-a","-l",NULL);       
       }                                                
      else 
      {                                                 
        printf("父进程开始运行,pid:%d\n",getpid());    
        int status = 0;                                                                                                                                                 
        pid_t res = waitpid(-1,&status,0);    
        if(res > 0)                      
        {                                
          printf("等待子进程成功\n");    
        }                    
                             
      }                      
                             
  }                          
  return 0;
}

如果不创建子进程,那么我们替换的进程只能是父进程。如果创建了子进程,那么我们替换的进程就是子进程,而就算替换的代码出现了什么问题这样也不会影响父进程。

为什么我们不能影响父进程
因为我们想让父进程聚焦在读取数据,解析数据,指派任务的功能上。

加载子进程程序替换之前,父子数据和代码的关系?
之前我们学到的是:父子进程代码共享,数据写时拷贝
当子进程加载新程序的时候,不就是一种写入吗?代码要不要写时拷贝,将父子代码分离
必须要!!!
这样父子进程在代码和数据上就彻底分离开了,虽然之前也并不冲突。

学习多种进程替换的函数
execv
在这里插入图片描述
使用:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM 64
int main()
{
  //while(1)
  //{   //1.首先肯定是要一直循环的,因为我们的xshell一直在运行
      //2.显示提示行
      //3.获取用户输入的字符串
      //4.对字符串进行解析
      //5.执行以下代码
      pid_t id = fork();
      if(id == 0)
      {
        printf("子进程开始运行,pid:%d\n",getpid());
        sleep(1);
        char* const _argv[NUM] = {
          (char*)"ls",
          (char*)"-a",
          (char*)"-l",
          NULL
        };
        execv("/usr/bin/ls",_argv);
        //printf("子进程开始运行,pid:%d\n",getpid());    
        //execl("/usr/bin/ls",_argv[0],_argv[1],_argv[2],NULL);
       }    
      else                                                                                                                                                              
      {
        printf("父进程开始运行,pid:%d\n",getpid());    
        int status = 0;    
        pid_t res = waitpid(-1,&status,0);    
        if(res > 0)
        {
          printf("等待子进程成功\n");
        }

      }
     
  //}
  return 0;
}

execv其实和execl差不多,知识把要执行的指令保存到数组中罢了。
在这里插入图片描述
然后通过传入指令数组给execv表示要执行的指令。
execlp
在这里插入图片描述
execlp会自己在环境变量PATH中进行查找,不用告诉该函数执行的程序在哪里,我们只要插入文件名即可。

使用:

#include <stdio.h>    
  #include <unistd.h>    
  #include <sys/types.h>    
  #include <sys/wait.h>    
  #define NUM 64    
  int main()    
  {    
    //while(1)    
    //{   //1.首先肯定是要一直循环的,因为我们的xshell一直在运行    
        //2.显示提示行    
        //3.获取用户输入的字符串    
        //4.对字符串进行解析    
        //5.执行以下代码    
        pid_t id = fork();    
        if(id == 0)    
        {    
          printf("子进程开始运行,pid:%d\n",getpid());    
          sleep(1);    
       char* const _argv[NUM] = {    
            (char*)"ls",    
            (char*)"-a",    
            (char*)"-l",    
            NULL    
          };    
          execlp("ls","ls","-a","-l",NULL);                                                                                                                             
          //execv("/usr/bin/ls",_argv);    
          //printf("子进程开始运行,pid:%d\n",getpid());    
          //execl("/usr/bin/ls",_argv[0],_argv[1],_argv[2],NULL);    
         }    
        else    
        {    
          printf("父进程开始运行,pid:%d\n",getpid());    
          int status = 0;    
          pid_t res = waitpid(-1,&status,0);
          if(res > 0)
          {
            printf("等待子进程成功\n");
          }
  
        }
       
    //}
    return 0;
  }

这里就有一个问题:
在这里插入图片描述
我们第一个参数已经传入ls了,为什么后面还要再传?
第一个ls:代表你要执行谁->找到它
第二个ls:代表你想怎么执行->让程序能知道你要传递什么选项。
execvp
在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM 64
int main()
{
  //while(1)
  //{   //1.首先肯定是要一直循环的,因为我们的xshell一直在运行
      //2.显示提示行
      //3.获取用户输入的字符串
      //4.对字符串进行解析
      //5.执行以下代码
      pid_t id = fork();
      if(id == 0)
      {
        printf("子进程开始运行,pid:%d\n",getpid());
        sleep(1);
        char* const _argv[NUM] = {  
          (char*)"ls",  
          (char*)"-a",       
          (char*)"-l",  
          NULL  
        };  
        execvp("ls",_argv);
        //execlp("ls","ls","-a","-l",NULL);                                                                                                                             
        //execv("/usr/bin/ls",_argv);                                                                              
        //printf("子进程开始运行,pid:%d\n",getpid());                                                              
        //execl("/usr/bin/ls",_argv[0],_argv[1],_argv[2],NULL);                                                    
       }                                                                                                           
      else                                                                                                         
      {                                                                                                            
        printf("父进程开始运行,pid:%d\n",getpid());                                                               
        int status = 0; 
        pid_t res = waitpid(-1,&status,0);
        if(res > 0)
        {
          printf("等待子进程成功\n");
        }

      }
     
  //}
  return 0;
}

execle
在这里插入图片描述
多了一个环境变量参数,表示该函数取用环境变量时用该envp[]数组的,而不使用当前环境。

思考一个问题:
如何用程序执行我们自己写的代码?
比如我们要执行一下,自己写的代码,我们要如何用程序执行呢?

#include <stdio.h>    
#include <string.h>    
#include <stdlib.h>    
                                                                                                                                                                        
int main(int argc, char* argv[])    
{    
  if(argc != 2)    
  {    
    printf("can not excuate\n");    
    exit(1);    
  }    
    
  if(strcmp(argv[1],"-a") == 0)    
  {    
    printf("hello a\n");    
  }    
  else if(strcmp(argv[1],"-b") == 0)    
  {    
    printf("hello b\n");    
  }    
  else if(strcmp(argv[1],"-c") == 0)    
  {    
    printf("hello c\n");    
  }    
  else    
  {    
    printf("default!\n");    
  }    
  return 0;    
}

Makefile一下形成多个可执行程序
在这里插入图片描述
改exec.c代码

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>    
#include <sys/wait.h>    
#define NUM 64    
const char* myfile = "/home/xiaolin/linux/2023/lesson17/mycmd";    
int main()    
{    
  //while(1)    
  //{   //1.首先肯定是要一直循环的,因为我们的xshell一直在运行    
      //2.显示提示行    
      //3.获取用户输入的字符串    
      //4.对字符串进行解析    
      //5.执行以下代码    
      pid_t id = fork();    
      if(id == 0)    
      {    
        printf("子进程开始运行,pid:%d\n",getpid());    
        sleep(1);    
        /*char* const _argv[NUM] = {    
          (char*)"ls",    
          (char*)"-a",    
          (char*)"-l",    
          NULL    
        };*/    
        /*execvp("ls",_argv);    
        execlp("ls","ls","-a","-l",NULL);    
        execv("/usr/bin/ls",_argv);    
        printf("子进程开始运行,pid:%d\n",getpid());                                                                                                                     
        execl("/usr/bin/ls",_argv[0],_argv[1],_argv[2],NULL);*/    
        execl(myfile,"mycmd","-a",NULL);
       }    
      else    
      {   
        printf("父进程开始运行,pid:%d\n",getpid());
        int status = 0;
        pid_t res = waitpid(-1,&status,0);
        if(res > 0)
        {
          printf("等待子进程成功\n");
        }

      }
     
  //}
  return 0;
}

运行结果:
在这里插入图片描述
我们看到确实成功了,程序运行的结果和linux运行的结果一致。
上面找到我们要的可执行程序文件用的是绝对路径,但是我们也可以用相对路径。
在这里插入图片描述
在这里插入图片描述
我们看到确实可以改成相对路径。

由上面的知识可知我们也可以用C语言的可执行程序,调用其它语言的可执行程序。

exec*函数功能其实就是加载器的底层接口。
其实OS就提供了一个execve函数,其余的函数都是对这个函数的封装。
在这里插入图片描述
之前的接口是系统提供的基本封装,用于满足不同的调用场景,

文章来源:https://blog.csdn.net/m0_67077469/article/details/135057771
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。