linux下的进程程序替换

进程程序替换

替换概念

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

大概意思就是程序替换只是把程序换了,仅仅是从1号程序换到了2号程序,修改了页表的映射地址,但是并没有创建新的PCB,因此没有创建新的进程。

这就是进程替换的概念!

替换函数

在linux手册里,有6个相似的函数,他们虽然参数列表不一样,但是功能一样。

  • 如果这些函数调用成功则加载新的程序,从启动代码开始执行,不再返回。
  • 如果出错则返回-1。
  • 所以exec函数只有出错的返回值而没有成功的返回值。

execl()

l:列表的形式传入参数

int extcl(const char * path, const char* argv,...);

参数:

path:是要打开可执行程序的路径

argv,... :这个语法表示可变的参数列表,可以传入多个不定个数的参数,最后一个参数传入NULL,表示参数传递完成。

描述不清楚,看个例子就明白了:

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>

  5 int main()
  6 {
  7   printf("当前进程开始!\n");
  8   //execl("/usr/bin/ls","ls","--color=auto","-l",NULL);
  9   //一旦调用成功,后续代码都不会执行了!!!                                                                                                                            
 10   execl("/usr/bin/ls","ls","--color=auto","-l", "-i",NULL);
 11   //execl("/usr/bin/top", "top", NULL);
 12   printf("当前进程结束!\n");                                                                                                                                   
 13   return 0;                                                                                                                                                     
 14 }      

因为Linux系统下一切皆文件,ls命令其实就是一个可执行程序,我们使用execl函数,执行这个程序。我们可以改变可变参数列表来使用ls命令不同的功能。

execv()

v:数组的形式传入参数

int extcv(const char * path, char *const argv\[\]);

参数:

path:仍然是文件路径。

char *argv\[\]:指针数组,以数组的形式传入。

为了更加适应实际的应用场景,我们使用父进程fork子进程,然后使用子进程进行进程替换,这样不会影响父进程的执行。

如下:

cpp 复制代码
7 #define NUM 32
  8 int main()
  9 {
 10   pid_t id = fork();
 11 
 12   if(id == 0)
 13   {
 14     //子进程
 15     // ls -a -l
 16     printf("子进程开始执行,pid = %d \n",getpid());
 17     //传入指针数组
 18     char *const _argv[NUM] = {
 19      (char*)"ls",
 20      (char*)"-l",
 21       (char*)"-i",
 22       NULL
 23     };
 24     execv("/usr/bin/ls",_argv);  //传入指针数组!!!
 25     exit(1);
 26   }
 27   else{
 28     //父进程
 29     printf("父进程开始等待,pid =%d \n", getpid());                                                                                                                     
 30     int status = 0;
 31     pid_t ret = waitpid(-1, &status, 0); //阻塞等待
 32     if(ret>0)
 33     {
 34       printf("等待成功,退出码为:%d \n", WEXITSTATUS(status));
 35     }
 36   }
 37   return 0;
 38 }

可以看到,子进程帮助父进程完成了执行ls命令,并且没有影响父进程的执行。

execvp()/execlp()

p:表明第一个参数不再是文件路径,而是通过环境变量就可以找到的程序。

看下面的例子更能清楚的解释:

execvp()的例子:

cpp 复制代码
  7 #define NUM 32
  8 int main()
  9 {
 10   pid_t id = fork();
 11 
 12   if(id == 0)
 13   {
 14     //子进程
 15     // ls -a -l
 16     printf("子进程开始执行,pid = %d \n",getpid());
 17     //传入指针数组
 18     char *const _argv[NUM] = {
 19      (char*)"ls",  
 20      (char*)"-l",  
 21       (char*)"-i",  
 22       NULL  
 23     };  
 24     //execv("/usr/bin/ls",_argv);  
 25     execvp("ls",_argv);   //因为环境变量的存在,我们可以只输入ls便可找到该文件。                                                                                        
 26     exit(1);                                             
 27   }                                                                                                                              
 28   else{                                                                                                                          
 29     //父进程                                                                                                                     
 30     printf("父进程开始等待,pid =%d \n", getpid());                                                                              
 31     int status = 0;                                                                                                              
 32     pid_t ret = waitpid(-1, &status, 0); //阻塞等待                                                                              
 33     if(ret>0)                                                                                                                    
 34     {                                                                                                                            
 35       printf("等待成功,退出码为:%d \n", WEXITSTATUS(status));                                                                   
 36     }                                                                                                                            
 37   }                                                                                                                              
 38   return 0;
 39 }

execlp()例子:

仅仅将上面的代码,25行换成26行的内容即可!

cpp 复制代码
   25     //execvp("ls",_argv);   //因为环境变量的存在,我们可以只输入ls便可找到该文件。                                                                   
   26     execlp("ls", "ls", "-l", "-i", NULL);        

execle()/execvpe()

e:可以维护自己的环境变量,将环境变量传给要替换的进程。

以execle为例:

将上面的子进程调用部分修改:

cpp 复制代码
    7 #define NUM 32
    8 int main()
    9 {
   10 
   11   //设置一个环境变量,为的是 让子进程拿到
   12   //因为环境变量具有全局属性
   13   char *const _env[NUM]={
   14     (char*)"xty=123456789",
   15     NULL
   16   };

   31     execle("./myexe", "mycmd", "-a", NULL, _env);   //修改成这样,//其中列表参数是随便传的,myexe是调用的可执行程序。
cpp 复制代码
myexe:
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 
  4 int main()
  5 {
  6                                                                                                                                                                         
  7   printf("得到的环境变量xty:%s\n", getenv("xty"));
  8   return 0;
  9 }

可知myexe获取到了环境变量。

如何在C/C++程序里面执行别的语言写的程序。

bash 复制代码
//使用这个命令即可运行
python test.py

首先写一个python文件脚本。待会我们就是用c程序执行它。

使用execlp()调用比较合适。

c 复制代码
  1 #include <stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h>
  6 int main()
  7 {
  8   pid_t id = fork();
  9   if(id == 0)
 10   {
 11     //子进程,调用别的语言写的程序
 12     execlp("python", "python", "test.py", NULL);
 13     exit(-1);
 14   }
 15   else{
 16     //父进程
 17     int status = 0;
 18     printf("父进程开始等待: \n");
 19     int ret = waitpid(-1, &status, 0); //阻塞等待
 20     if(ret > 0)
 21     {
 22       printf("父进程等待成功,子进程退出码为:%d \n",WEXITSTATUS(status));
 23 
 24     }
 25   }                                                                                                                                                                     
 26   return 0;
 27 }

我们可以看出python脚本被我们成功调用!

小tips

我们在运行py脚本时,需要运行python这个程序。因为python是一个解释器,我们需要使用解释器来解释test.py文件。

bash 复制代码
python test.py

如果我们给该test.py文件加上执行(x)权限,那么我们这样即可运行该文件:

因此将上面的execlp调用参数,修改为下面这样也可以:

c 复制代码
 //12     execlp("python", "python", "test.py", NULL);
 12     execlp("./test.py", "test.py", NULL);

上面就是程序替换的相关知识,更深入的内容还请读者自行查阅和学习。

相关推荐
大树881 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠1 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质1 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush41 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5202 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz2 小时前
Maven依赖冲突
java·服务器·maven
Inhand陈工2 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智3 小时前
ARP代理--工作原理
运维·网络·arp·arp代理
不会C语言的男孩3 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
shushangyun_3 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化