欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > 【Linux】进程替换

【Linux】进程替换

2024/11/29 18:31:54 来源:https://blog.csdn.net/m0_72532428/article/details/143928040  浏览:    关键词:【Linux】进程替换

进程替换

    • 一、概念
    • 二、原理
    • 三、替换函数
      • 1. execl
      • 2. execlp
      • 3.execle
      • 4.execv
      • 5.execvp
      • 6.execvpe
    • 四、实现一个简易的shell

一、概念

当我们fork()生成子进程后,子进程的代码与数据可以来自其他可执行程序。把磁盘上其他程序的数据以覆盖的形式给子进程。这样子进程就可以执行全新的程序了,这种现象称为程序替换。

二、原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

三、替换函数

1. execl

①函数原型:

int execl(const char*path,const char*arg,...)

②参数解析:

  • path:为可执行程序的路径;
  • arg:如何执行这个可执行程序(执行该程序的指令);
  • … :指的是给这执行程序携带的参数,在参数末尾加NULL表示参数结束;
  • 返回值:函数调用失败返回-1,成功不返回

③示例:

替换成功:

#include<stdio.h>
#include<unistd.h>
int main()
{printf("before\n");execl("/usr/bin/ls","ls","-a","-l",NULL);printf("after\n");return 0;
}

在这里插入图片描述

替换失败:

#include<stdio.h>
#include<unistd.h>
int main()
{printf("before\n");execl("/usr/bin/la","ls","-a","-l",NULL);printf("after\n");return 0;
}

在这里插入图片描述

通过对比可以发现,替换成功以后,原程序后面的代码就不执行了,执行另一个程序 的代码,失败就接着执行原程序的代码。

2. execlp

①函数原型:

 int execlp(const char *file, const char *arg, ...);

②参数解析:

  • file:要替换的目标程序;
  • arg:如何执行这个程序,…为给这个程序传的参数;
  • 返回值:函数调用失败返回-1;

③示例:

比较于execl,execp默认在Linux环境变量PATH中查找可执行程序,所以传参可直接传可执行程序的名字,不用传绝对路径。

#include<stdio.h>
#include<unistd.h>
int main()
{printf("before\n");execlp("ls","ls","-a","-l",NULL);printf("after\n");return 0;
}

在这里插入图片描述

3.execle

①函数原型:

int execle(const char *path, const char *arg, ..., char * const envp[]);

②参数解析:

  • path:替换目标程序路径;
  • arg:如何执行这个程序,…为给这个程序传的参数;
  • envp数组:要导入的环境变量;
  • 返回值:失败返回-1;

③示例:

查看环境变量代码,用来验证后面的结果

#include<stdio.h>
#include<stdlib.h>
int main()
{printf("path:%s\n",getenv("PATH"));printf("aaa:%s\n",getenv("aaa"));return 0;
}

在这里插入图片描述

#include<stdio.h>
#include<unistd.h>
int main()
{char* envp[]={"aaa=hello",NULL};printf("before\n");execle("./a.out","./a.out",NULL,envp);printf("after\n");return 0;
}

在这里插入图片描述

注意:

  1. 导环境变量的数组最后以NULL结尾
  2. 导入环境变量后原系统环境变量的值被清空,这种导入环境变量的方式为覆盖式导入

4.execv

①函数原型:

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

②参数解析:

  • path:替换目标程序路径;
  • argv数组:保存的是参数列表,将如何执行 可执行程序 和可执行程序需要的参数保存到字符串数组中,最后以NULL结尾表示参数结束;
  • 返回值:进程替换失败返回-1

③示例:

#include<stdio.h>
#include<unistd.h>
int main()
{char* argv[]={"ls","-a","-l",NULL};printf("before\n");execv("/usr/bin/ls",argv);printf("after\n");return 0;
}

在这里插入图片描述

注:可以发现argv数组与main函数的命令行参数相同

5.execvp

①函数原型:

int execvp(const char *file, char *const argv[]);

②参数解析:

  • file:要替换的目标程序;
  • argv数组:保存的是参数列表,将如何执行 可执行程序 和可执行程序需要的参数保存到字符串数组中,最后以NULL结尾表示参数结束;

③示例:略

6.execvpe

①函数原型:

 int execvpe(const char *file, char *const argv[], char *const envp[]);

②参数解析:

  • file:要替换的目标程序;
  • argv数组:保存的是参数列表,将如何执行 可执行程序 和可执行程序需要的参数保存到字符串数组中,最后以NULL结尾表示参数结束;
  • envp数组:要导入的环境变量;

③示例:略

小结:

替换函数前面的exec不变

  • l:参数采用列表
  • v:参数采用数组
  • p:不需要输入路径,在环境变量自动搜索
  • e:要导入自己的环境变量

所以execvp表示不需要输入路径,参数用数组传
execve表示需要输入路径,参数用数组传,自己维护环境变量

四、实现一个简易的shell

#include<stdio.h>
#include<unistd.h>
#include<assert.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>
#define MAX 1024
#define ARGC 64
#define SEP " "//将输入的字符串切割并保存到argv中
int split(char*commandstr,char*argv[])
{assert(commandstr);assert(argv);argv[0]=strtok(commandstr,SEP);if(argv[0]==NULL)return -1; //若为NULL,则重新输入int i=1;while(argv[i++]=strtok(NULL,SEP));return 0;
}
int main()
{ while(1){char commandstr[MAX]={NULL}; //用于保存用户输入的指令char*argv[ARGC]={NULL};printf("[lx@hecs-%d myshell]$ ",getpid());fflush(stdout);char*s=fgets(commandstr,sizeof(commandstr),stdin); //获取指令assert(s);(void)s;commandstr[strlen(commandstr)-1]='\0'; //去掉键盘输入的'\n'int n=split(commandstr,argv);  //切割输入的指令字符串if(n!=0)continue;pid_t id=fork();if(id==0){execvp(argv[0],argv); //程序替换exit(0);}int status=0;waitpid(id,&status,0); //等待子进程}return 0;
}

在这里插入图片描述

可以看到简易版的myshell就实现好了。

myshellplus

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <assert.h>#define MAX 1024
#define ARGC 64
#define SEP " "int split(char* commandstr,char* argv[])
{assert(commandstr);assert(argv);argv[0] = strtok(commandstr,SEP);if(argv[0] == NULL) return -1;int i = 1;while((argv[i++] = strtok(NULL,SEP)));return 0;
}void showEnv()
{extern char** environ;for(int i = 0; environ[i]; i++) printf("%d:%s\n",i,environ[i]);
}int main()
{extern int putenv(char* string);char myenv[32][256];int env_index = 0;int exitCode = 0;while(1){char commandstr[MAX] = {0};char* argv[ARGC] = {NULL};printf("[hxy@mychaimachine]$ ");fflush(stdout);char* s = fgets(commandstr,sizeof(commandstr),stdin);assert(s);(void)s;commandstr[strlen(commandstr) - 1] = '\0'; // 去掉键盘输入的\nint n = split(commandstr,argv); // 切割字符串if(n != 0) continue;if(strcmp(argv[0],"cd") == 0){if(argv[1] != NULL) chdir(argv[1]);continue;}else if(strcmp(argv[0],"export") == 0){if(argv[1] != NULL){strcpy(myenv[env_index],argv[1]); // 用户自己定义的环境变量,需要bash自己来维护putenv(myenv[env_index++]);}continue;}else if(strcmp(argv[0],"env") == 0){showEnv(); // env查看环境变量时,其实看的是父进程bash的变量continue;}else if(strcmp(argv[0],"echo") == 0){const char* target_env = NULL;if(argv[1][0] == '$'){if(argv[1][1] == '?'){printf("%d\n",exitCode);continue;} else target_env = getenv(argv[1] + 1);if(target_env != NULL) printf("%s = %s\n",argv[1] + 1,target_env);}continue;}// ls设置颜色选项if(strcmp(argv[0],"ls") == 0){int pos = 0;while(argv[pos] != NULL){pos++;}argv[pos++] = (char*)"--color=auto";argv[pos] = NULL;}pid_t id = fork();if(id == 0){// 子进程execvp(argv[0],argv);exit(1);}int status = 0;pid_t ret = waitpid(id,&status,0);if(ret > 0){exitCode = WEXITSTATUS(status); // 获取最近一次进程的退出码}}return 0;
}

进程替换的知识就讲到这了,如有错误还望指出,886!!!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com