前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >进程控制第二弹(进程程序替换)

进程控制第二弹(进程程序替换)

作者头像
南桥
发布2024-04-22 08:50:51
770
发布2024-04-22 08:50:51
举报
文章被收录于专栏:南桥谈编程

代码现象

代码语言:javascript
复制
#include<stdio.h>    
#include<unistd.h>    
    
int main()    
{    
  printf("testexec begin! ...\n");    
    
  execl("/usr/bin/ls","ls","-l","-a",NULL);                                                                                              
    
  printf("testexec end! ...\n");    
  return 0;    
} 

程序运行后,调用execl函数后,我们的程序去执行了ls命令,原来的进程中printf("testexec end! ...\n"); 没有执行。

基本原理

当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变

我们知道,进程=内核数据结构+代码数据 程序替换的本质是将当前进程的代码和数据进行替换。

替换的时候,会不会创建新的进程? 答案是没有!!只不过是拿老程序的壳子执行新程序的代码。

站在被替换进程的角度:本质上是这个程序被加载到内存。使用exec系列函数加载,exec系列函数类似一种Linux上的加载函数。

所以为什么上述现象中,原来的进程中printf("testexec end! ...\n"); 没有执行的原因是,调用execl函数后,去执行ls程序了,原来的代码和数据被替换了。

exec系列函数执行完毕后,后续的代码不见了,因为被替换了,因此没有机会去执行了。

不用关心exec系列函数的返回值,只要替换成功,就不会向后面执行;反之,一定是替换失败。

多进程版本

实例

fork创建子进程,让子进程自己去替换

代码:

代码语言:javascript
复制
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
  printf("testexec begin! ...\n");
              
  pid_t id=fork();    
  if(id==0)                           
  {    
    sleep(2);         
    //child    
    execl("/usr/bin/ls","ls","-l","-a",NULL);    
    exit(1);                                                                                               
  }            
                                                 
  //father    
  int status=0;    
  pid_t rid=waitpid(id,&status,0);    
  if(rid>0)    
  {                
    printf("father wait success,child,exit code:%d\n",WEXITSTATUS(status));    
  }          
  printf("testexec end! ...\n");                                               
  return 0;
}

现象:

基本原理

创建子进程,子进程完成的任务:

  1. 让子进程执行父进程代码的一部分
  2. 让子进程执行一个全新的程序

首先父进程和子进程的PCB、虚拟内存构建好后,通过页表映射到物理内存中。可执行程序testexecl从磁盘中加载到物理内存中。在代码中,子进程执行一个新的程序execl("/usr/bin/ls","ls","-l","-a",NULL);此时有一个ls程序需要从磁盘中加载到物理内存中。之前说过,进程具有独立性,即便是父子进程。将ls加载到物理内存时,需要在数据层面上做写时拷贝,然后把ls数据加载进去,修改子进程的映射关系,保证子进程和父进程在数据层面上是独立的。但是ls不仅仅只有数据,还有代码,因此代码也需要发生写时拷贝。虽然代码是可读的,但是在操作系统看来都无所谓。所以重新开辟内存,将ls代码加载到物理内存,修改子进程的映射关系。至此,只要程序替换成功,彻底将子进程和父进程分开了。

使用所有的替换方法,并且认识函数的参数含义

execl

代码语言:javascript
复制
int execl(const char *path, const char *arg, ...);

execl中,l:list,列表

path:需要执行的路劲,需要带路劲 后面的参数:在命令行中怎么执行

例如:

代码语言:javascript
复制
 execl("/usr/bin/ls","ls","-l","-a",NULL);

execv

代码语言:javascript
复制
execv(const char *path, char *const argv[]);

v(vector) : 参数用数组

代码语言:javascript
复制
 if(id==0)    
    {    
      sleep(2);    
      char* const argv[]={"ls","-l","-a","--color",NULL};    
      //child    
     // execl("/usr/bin/ls","ls","-l","-a",NULL);    
      execv("/usr/bin/ls",argv);                                                                                                         
      exit(1);    
    }    

execlp、execvp

代码语言:javascript
复制
execlp(const char *file, const char *arg, ...);
代码语言:javascript
复制
execvp(const char *file, char *const argv[]);

p(path) : 有p自动搜索环境变量PATH,用户可以不传要执行的路劲(但是文件名要传),直接告诉要执行谁即可

代码语言:javascript
复制
if(id==0)    
    {    
      sleep(2);    
      char* const argv[]={"ls","-l","-a","--color",NULL};    
      //child    
     // execl("/usr/bin/ls","ls","-l","-a",NULL);    
     // execv("/usr/bin/ls",argv);    
      execvp("ls",argv);                                                                                                                 
      exit(1);    
    }    

execvpe

上面的程序替换,我们替换的都是系统的命令,那么可不可以替换我们自己写的程序呢?

代码语言:javascript
复制
int execvpe(const char *file, char *const argv[],char *const envp[]);

e(env) : 表示自己维护环境变量

testexec二进制程序去执行mypragma二进制程序

mypragma.cc代码:

代码语言:javascript
复制
 if(id==0)    
  {    
    sleep(2);    
    execl("./mypragma","mypragma",NULL);                                                                                                 
   // sleep(2);    
   // char* const argv[]={"ls","-l","-a","--color",NULL};    
    //child    
   // execl("/usr/bin/ls","ls","-l","-a",NULL);    
   // execv("/usr/bin/ls",argv);    
   // execvp("ls",argv);    
    exit(1);    
  }    

此时,我们写的C++程序就被调度了

除了C++语言可以被C语言调度,其他语言也可以被调度,例如python、脚本语言等…

我们知道了这一件事情之后,再谈execvpe函数:

testecel.c文件部分代码:

代码语言:javascript
复制
if(id==0)    
  {    
    char* const argv[]={(char*)"mypragma",NULL};    
    char* const envp[]={(char*)"HAHA=111",(char*)"HEHE=222",NULL};    
    sleep(2);    
   // execl("./mypragma","mypragma",NULL);    
    execvpe("./mypragma",argv,envp);                                                                                                     
    
   // sleep(2);    
   // char* const argv[]={"ls","-l","-a","--color",NULL};    
    //child    
   // execl("/usr/bin/ls","ls","-l","-a",NULL);    
   // execv("/usr/bin/ls",argv);    
   // execvp("ls",argv);    
    exit(1);    
  }    

mypragma.cc代码:

代码语言:javascript
复制
 #include<iostream>    
      
  using namespace std;    
      
W>int main(int argc,char* argv[],char* env[])    
  {    
    int i=0;    
    for(;argv[i];i++)    
    {    
      printf("argv[%d]:%s\n",i,argv[i]);    
    }    
      
    printf("-----------------------------------\n");    
    for(i=0;env[i];i++)    
    {    
      printf("env[%d]:%s\n",i,env[i]);                                                                                                   
    }    
    printf("-----------------------------------\n");    
    cout<<"hellp C++,I am a C++ pragma!!"<<endl;    
    cout<<"hellp C++,I am a C++ pragma!!"<<endl;    
    cout<<"hellp C++,I am a C++ pragma!!"<<endl;    
    cout<<"hellp C++,I am a C++ pragma!!"<<endl;    
    cout<<"hellp C++,I am a C++ pragma!!"<<endl;    
      
    return 0;    
  } 

运行结果:

结论:我们平时自己运行的程序,命令行参数和环境变量是父进程给你的,父进程自己有一个环境变量表,创建子进程时把对应的信息传递给子进程,execvpe直接交给子进程,环境变量就直接给了子进程。

父进程本身就有一批环境变量,从“爷爷进程”来的,即bash

这个传参,如果传的是自定义的环境变量,那么就整体替换所有环境变量

传环境变量有三种情况:

  1. 用全新的给子进程
  2. 用老的环境变量给子进程,environ
  3. 老的环境变量稍作修改,传递给子进程

总结

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码现象
  • 基本原理
  • 多进程版本
    • 实例
      • 基本原理
      • 使用所有的替换方法,并且认识函数的参数含义
        • execl
          • execv
            • execlp、execvp
              • execvpe
              • 总结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档